Documentation
Object storage

CORS

Configure CORS rules on a Sliplane Object Storage bucket so browsers on your own domains can read from and upload to it directly.

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() or XMLHttpRequest
  • reading a response header such as ETag after an upload

Server-side SDKs and background jobs do not need CORS rules.

Add a CORS rule

  1. Open Object Storage in the dashboard and select your bucket.
  2. Go to the Settings tab and find the CORS card.
  3. Click Add Rule.
  4. Fill in the rule and click Save.

To change a rule later, click it in the list. To delete it, use the remove button next to it and confirm.

CORS cannot be changed while a bucket is queued for deletion.

Rule fields

FieldMeaning
Allowed OriginsWhere the browser is running. Required.
Allowed MethodsWhich requests the browser may make: GET, PUT, POST, DELETE, HEAD. Required.
Allowed HeadersRequest headers the browser may send.
Exposed HeadersResponse headers your JavaScript may read.
Max AgeHow long the browser caches the preflight response, in seconds.

Allowed Origins

An origin is a scheme and a host, with no path, query, or fragment:

https://app.example.com
http://localhost:3000

Two wildcards are accepted:

  • * allows any origin
  • a leading *. matches all subdomains, for example https://*.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.

* lets any website on the internet make the requests the rule permits. Use it for public read-only assets, not for rules that allow PUT, POST, or DELETE.

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

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

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

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

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:

FieldValue
Allowed Originshttps://app.example.com
Allowed MethodsPUT, GET, HEAD
Allowed HeadersContent-Type
Exposed HeadersETag
Max Age3600

If you also run the app locally against the same bucket, add http://localhost:3000 as a second origin in the same rule.

Limits

LimitValue
Rules per bucket10
Values per field25
Max age0 to 86400 seconds (24 hours)

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

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.

API

See the bucket API reference.

How is this guide?

On this page