Skip to content

CORS

CORS is a rule a browser applies before a web page may read something from another address. The page at app.plune.ai and your bucket are two different addresses, so the browser asks the bucket “may this page read you?” and reads the answer only if the bucket says yes. The bucket says yes when it has a CORS rule that names the page’s address.

FileNeeds a CORS ruleWhy
Screenshotnoshown with an <img> tag, and a tag does not read the file, it only displays it
Videonoshown with a <video> tag, the same
Text, JSON, logyesthe dashboard reads the file itself, to show it in a window
Traceyesthe dashboard reads the file itself and hands it to the trace viewer in the window; the viewer’s own address needs no rule
A person adding a file in the dashboardyesthe browser sends the file to the bucket itself, by a link Plune signed for it
The CI uploadingnoit uploads from a machine, not from a browser

So a bucket with no rule at all works, and shows screenshots and videos; text files and traces will refuse to open — and a file a person adds in the dashboard does not get there: its row says the connection dropped. Settings → Storage tells you which: its check CORS lets … read and add files fails with CORS missing when the dashboard may not read. With #1341 it asks about putting a file too: a rule without PUT, or without the content-type header, fails it with CORS put missing, and How to fix opens the rule.

One rule, and as little in it as possible: GET and PUT, the one header content-type, and only from the address of the dashboard.

DashboardAddress to allow
Productionhttps://app.plune.ai
Betahttps://beta.plune.ai

Do not allow * (every address), and do not allow DELETE: a browser reads files and puts a person’s new ones, each by a link Plune signed for that one file — the rule lets the page send the request, the link is the right to make it. The CI does not go through CORS.

If one bucket serves both the beta and production dashboards, list both addresses in AllowedOrigins.

Both take the same JSON in their consoles:

[
{
"AllowedOrigins": ["https://app.plune.ai"],
"AllowedMethods": ["GET", "PUT"],
"AllowedHeaders": ["content-type"],
"MaxAgeSeconds": 3600
}
]

Where to paste it: AWS S3, Cloudflare R2.

Every service has its own place for the rule, and some a different format; they are on Other S3-compatible storage, under each service. A service that can only take the S3 API’s XML wants the same content: the origin, GET and PUT, the header and an age.

In Settings → Storage press Test connection. The check CORS lets <address> read and add files asks your bucket what it would tell a browser at the dashboard’s address, the way a browser asks: whether it may read (GET), and then — with #1341 — whether it may put a file with its content-type (PUT). It passes when both answers are yes. (The other checks write a small test file into your folder and delete it again.)

A rule can take a moment to apply — a few seconds, and in the rare case up to half a minute on R2 — so if the check fails right after you saved the rule, wait and press Test connection again.