CORS
Browsers block JavaScript from reading a response that comes from a different origin than the page, unless the response says the origin is allowed. CORS rules on a bucket are what produce that permission.
You need a CORS rule when browser code talks to the bucket endpoint directly, for example:
- uploading a file straight from the browser with a presigned URL
- fetching an object with
fetch()orXMLHttpRequest - reading a response header such as
ETagafter an upload
You do not need one when your server, a background job, or an S3 SDK outside the browser talks to the bucket, or when the browser just loads an object through an <img>, <video>, or <script> tag.
Add a CORS rule
Section titled “Add a CORS rule”- Open Object Storage in the dashboard and select your bucket.
- Go to the Settings tab and find the CORS card.
- Click Add Rule.
- Fill in the rule and click Save.
Rules are saved immediately, one request per change. There is no separate save step at the bottom of the page.
To change a rule later, click it in the list. To delete it, use the remove button next to it and confirm.
Rule fields
Section titled “Rule fields”| Field | Meaning |
|---|---|
| Allowed Origins | Where the browser is running. Required. |
| Allowed Methods | Which requests the browser may make: GET, PUT, POST, DELETE, HEAD. Required. |
| Allowed Headers | Request headers the browser may send. |
| Exposed Headers | Response headers your JavaScript may read. |
| Max Age | How long the browser caches the preflight response, in seconds. |
Allowed Origins
Section titled “Allowed Origins”An origin is a scheme and a host, with no path, query, or fragment:
https://app.example.comhttp://localhost:3000Two wildcards are accepted:
*allows any origin- a leading
*.matches all subdomains, for examplehttps://*.example.com
A wildcard anywhere else is rejected. Only http:// and https:// origins are allowed, and a trailing slash is stripped before the origin is stored.
Allowed Methods
Section titled “Allowed Methods”Pick the methods your browser code actually issues. Reading objects needs GET, and usually HEAD as well. Uploading with a presigned URL needs PUT. Browser-side deletes need DELETE.
Allowed Headers
Section titled “Allowed Headers”These are the request headers the browser is allowed to send, the ones it asks about in the Access-Control-Request-Headers preflight. Uploads typically need at least Content-Type.
* allows all headers, and a trailing wildcard matches a prefix, for example x-amz-*. A header name cannot contain spaces or separators such as : or /.
Exposed Headers
Section titled “Exposed Headers”By default the browser only hands a few response headers to your code. Anything else has to be listed here. An upload that verifies the returned ETag needs ETag exposed.
Wildcards are not allowed in exposed headers, so list each header by name.
Max Age
Section titled “Max Age”The number of seconds a browser may cache the preflight response before asking again. 0 leaves the decision to the browser. New rules start at 3600.
Example: browser uploads with presigned URLs
Section titled “Example: browser uploads with presigned URLs”A single-page app on https://app.example.com uploads files with presigned PUT URLs generated by your backend, and checks the ETag it gets back:
| Field | Value |
|---|---|
| Allowed Origins | https://app.example.com |
| Allowed Methods | PUT, GET, HEAD |
| Allowed Headers | Content-Type |
| Exposed Headers | ETag |
| Max Age | 3600 |
If you also run the app locally against the same bucket, add http://localhost:3000 as a second origin in the same rule.
Limits
Section titled “Limits”| Limit | Value |
|---|---|
| Rules per bucket | 10 |
| Values per field | 25 |
| Max age | 0 to 86400 seconds (24 hours) |
Changes made outside the dashboard
Section titled “Changes made outside the dashboard”An access key has full control over its own bucket, which includes the bucket’s CORS configuration. Any S3 client can therefore overwrite these rules with PutBucketCors, and delete them with DeleteBucketCors.
The dashboard reads the live configuration from the bucket every time you open the settings page, so rules written by an S3 client show up there and can be edited like any other rule.
Troubleshooting
Section titled “Troubleshooting”The browser reports a CORS error. Check the origin in the error message against the rule. The scheme and the port are part of the origin: http://localhost:3000 does not match https://localhost:3000 or http://localhost.
A preflight request fails. The method and every header your code sends must be covered by the same rule. A request that sends Content-Type needs Content-Type (or *) in Allowed Headers.
Your code cannot read a response header. Add the header to Exposed Headers.
An old rule still seems to apply. Browsers cache preflight responses for the length of Max Age. Reload with the developer tools open and caching disabled, or wait it out.
The Sliplane API exposes the same configuration:
| Method | Endpoint | Description |
|---|---|---|
GET | /buckets/{bucketId}/cors | Read the CORS configuration |
PUT | /buckets/{bucketId}/cors | Replace all rules |
DELETE | /buckets/{bucketId}/cors | Remove all rules |
PUT replaces the whole configuration, so send every rule you want to keep. To remove the last rule, use DELETE instead of sending an empty rule set.
Open the API documentation for the full request and response schema.