Let websites use a bucket with CORS rules
Choose which websites may call a bucket from a browser, with which methods and headers, and read the rules back with any S3 client.
In the dashboard
A CORS rule tells browsers which other websites may use a bucket. When a page on https://example.com uploads to a bucket or reads from it with JavaScript, the browser first asks the bucket whether that site may. The bucket answers from its CORS rules, and the browser blocks the request when no rule allows it.
You set CORS rules on the CORS card in the bucket's Settings tab, or with the API. S3 clients can read the rules and cannot change them.
Before you begin
Section titled Before you begin- Sign in to the dashboard and open the service. The service must be
activeto change rules. While it waits for payment or is suspended, you can read them. - Know each site's origin: its scheme, its host and its port when it is not the usual one, such as
https://example.comorhttps://app.example.com:8443. An origin has no path and no trailing slash.
How the bucket answers browsers
Section titled How the bucket answers browsersThe rules apply at the bucket's S3 endpoint, such as https://s3.fra.coritan.com:7337/u7-assets. If the bucket is public, they also apply at its public URL and custom domains.
With no rules:
- Pages on any website can send signed or presigned requests to the S3 endpoint from a browser. Amazon S3 and Cloudflare R2 refuse these requests until a bucket has rules.
- The public URL and custom domains answer no other website. A page elsewhere can still show an image or play a video from them, but it cannot read an object with
fetch.
With rules:
- A browser request uses the first rule that matches its origin, its method and every header it sends. When no rule matches, the browser blocks the request.
- Each request to the S3 endpoint still needs a signature or a presigned URL. A rule only lets the browser send it.
- A public URL or custom domain answers
GETandHEADonly, whatever the rule allows.
Changes apply within a minute.
The dashboard's own rule
Section titled The dashboard's own ruleThe dashboard's file browser uploads to your bucket from https://www.coritan.com. So that it keeps working once you add rules, we put one more rule in front of yours. Its ID is coritan-dashboard, it allows https://coritan.com and https://www.coritan.com, and it does not appear on the CORS card.
S3 holds at most 100 CORS rules on a bucket, and this rule takes one of them, so you can add up to 99. An S3 client that reads the bucket's CORS sees coritan-dashboard first (Read the rules with an S3 client). You cannot give a rule of your own that ID.
Add a rule
Section titled Add a rule- Open the service, select the Buckets tab, then select the bucket.
- Select the Settings tab. The CORS card lists the bucket's rules.
- Select Add rule….
- In Allowed origins, type each origin on a line of its own, such as
https://example.com. A*alone allows every website. One*in a host allows its subdomains, as inhttps://*.example.com. - Under Allowed methods, tick what pages on those sites may do.
GETandHEADread,PUTandPOSTupload, andDELETEremoves. - In Allowed headers, list the request headers a page may send, one per line, such as
content-type. One*in a name matches many headers, as inx-amz-meta-*, and*alone allows every header. A browser upload usually sendscontent-type, and an S3 library also sendsauthorizationand severalx-amz-headers, so*is the simplest choice. - In Exposed headers, list the response headers your JavaScript needs to read, such as
ETag. A multipart upload from a browser needsETag. This field does not take*. - Optionally, set Max age in seconds. A browser keeps the answer to its preflight check for that long and does not ask again for each request.
- Optionally, give the rule a Rule ID to tell it apart from the others.
- Select Add rule.
A message confirms it: CORS rule added. It applies within a minute.
Result
Section titled ResultThe rule appears on the CORS card with its Origins, Methods and Headers, and the card says how many rules the bucket has out of 99. Rules are listed in the order browsers check them, and a new rule goes last.
To check a rule, open your site and repeat the request. In the browser's developer tools, the request's preflight (an OPTIONS request) answers with Access-Control-Allow-Origin set to your origin, or to * for a rule that allows every website.
Change or delete a rule
Section titled Change or delete a ruleEach rule's menu has Edit rule… and Delete rule…. Edit rule… opens the same fields as Add rule…; select Save rule to keep the change. Delete rule… asks you to confirm with Delete rule.
Deleting the last rule takes the bucket back to having no rules (How the bucket answers browsers).
Edit every rule as JSON
Section titled Edit every rule as JSONYou can paste rules written for Amazon S3 or Cloudflare R2.
- On the CORS card, open the menu at the top right and select Edit as JSON….
- In CORS rules, paste the rules. The box takes:
- S3's CORS XML, starting with
<CORSConfiguration>. - JSON as
aws s3api get-bucket-corsprints it, with aCORSRuleslist. - R2's CORS JSON, with
allowedandexposeHeadersin each rule.
- S3's CORS XML, starting with
- Select Save rules.
Saving replaces every rule on the bucket, and an empty box removes them all. When the pasted rules include the coritan-dashboard rule, we leave it out, since the dashboard keeps its own.
To remove every rule at once, select Remove every rule… in the same menu and confirm with Remove rules.
Read the rules with an S3 client
Section titled Read the rules with an S3 clientAny access key whose scope covers the bucket can read its CORS rules, read only keys included. With the AWS CLI:
aws s3api get-bucket-cors --bucket u7-assets \
--endpoint-url https://s3.fra.coritan.com:7337
{
"CORSRules": [
{
"ID": "coritan-dashboard",
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "HEAD", "DELETE"],
"AllowedOrigins": ["https://coritan.com", "https://www.coritan.com"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3000
},
{
"ID": "web-app",
"AllowedHeaders": ["content-type"],
"AllowedMethods": ["GET", "PUT"],
"AllowedOrigins": ["https://example.com"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3600
}
]
}
A bucket with no rules answers NoSuchCORSConfiguration. Each read is a Class B request (Usage and billing).
PutBucketCors and DeleteBucketCors answer 403 AccessDenied with every access key. Change the rules in the dashboard or with the API (With the API).
Troubleshooting
Section titled Troubleshooting- The browser console says the request was blocked by CORS policy
- No rule matched the request. Check that a rule lists the page's exact origin, scheme and port included, the method, and every header the page sends. A rule for
https://example.comdoes not coverhttps://www.example.com. For a public URL or custom domain, the bucket needs a rule even for a plainGET. CORS rule 1: https://example.com/app is not an origin. Write a scheme and a host, such as https://example.com, with no path or trailing slash.- Remove the path and any trailing slash from the origin. The number says which rule is at fault.
CORS rule 1: PATCH is not a method a rule can allow. Use GET, PUT, POST, DELETE or HEAD.- A pasted or API rule names a method S3 does not offer. Remove it.
CORS rule 1: an exposed header cannot use *. Name each header.- List each response header your page reads, such as
ETag. CORS rule 1: the ID coritan-dashboard belongs to the dashboard. Pick another.- Give the rule another ID. In the API, leave the dashboard's rule out of the list you send.
A bucket can have at most 99 CORS rules. The dashboard keeps one more so its file browser can upload.- Merge rules that allow the same methods and headers into one rule with several origins. Add rule… is greyed out while the bucket has 99.
These CORS rules are too long. Keep them under 64 KB in all.- Use fewer rules, or a
*in an origin or header to cover several at once. This is not JSON or XML. Paste the rules as S3 writes them.- The text in Edit as JSON… is neither. Paste the whole document, including its first
{,[or<. This service is suspended; it can be changed once it is active- Pay the overdue invoice (Failed payments and suspended services), then try again. The message names the status
pendingwhile the service waits for payment. Too many requests for this action. Try again later.- Your account made 60 CORS and lifecycle rule changes in the last hour. Wait, then try again.
The bucket would not take these CORS rules. Try again in a minute.- The bucket's region could not save the rules, and nothing changed. Try again. If it keeps happening, contact support with the whole message.
Related
Section titled Related- Delete objects on a schedule with lifecycle rules
- Connect an S3 client
- S3 API compatibility
- Object Storage limits
With the API
Section titled With the APIEach request takes the service ID and the bucket ID, from GET /api/v1/client/object-storage/{service_id}/buckets. A service that is not Object Storage on your account answers 404 with Object storage service not found, and a bucket that is not on the service answers 404 with Bucket not found.
Read the rules
Section titled Read the rulesGET /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/cors returns the bucket's rules in the order browsers check them:
curl https://api.coritan.com/api/v1/client/object-storage/1207/buckets/31/cors \
-H "Authorization: Bearer $CORITAN_TOKEN"
{
"bucket_id": 31,
"rules": [
{
"id": "web-app",
"allowed_origins": ["https://example.com"],
"allowed_methods": ["GET", "PUT"],
"allowed_headers": ["content-type"],
"expose_headers": ["ETag"],
"max_age_seconds": 3600
}
],
"max_rules": 99
}
The list never holds the dashboard's rule. max_rules is how many rules the bucket can have.
Replace the rules
Section titled Replace the rulesPUT /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/cors replaces every rule with the list you send:
curl -X PUT https://api.coritan.com/api/v1/client/object-storage/1207/buckets/31/cors \
-H "Authorization: Bearer $CORITAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"rules": [{"id": "web-app", "allowed_origins": ["https://example.com", "https://*.example.com"], "allowed_methods": ["GET", "PUT"], "allowed_headers": ["content-type"], "expose_headers": ["ETag"], "max_age_seconds": 3600}]}'
It answers 200 with the rules as saved, in the shape above. We write methods in capitals and drop repeated values. id, allowed_headers, expose_headers and max_age_seconds are optional. An empty rules list removes every rule.
Remove every rule
Section titled Remove every ruleDELETE /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/cors removes them all and answers with an empty rules list:
curl -X DELETE https://api.coritan.com/api/v1/client/object-storage/1207/buckets/31/cors \
-H "Authorization: Bearer $CORITAN_TOKEN"
Errors
Section titled Errors| Status | detail |
Cause |
|---|---|---|
409 |
This service is pending; it can be changed once it is active |
The service is not active. The message names its status. |
422 |
A message that names the rule, such as CORS rule 2: PATCH is not a method a rule can allow. Use GET, PUT, POST, DELETE or HEAD. |
A rule breaks S3's rules. Troubleshooting lists the common ones. |
422 |
A list of fields | The body is not in the shape above. |
429 |
An object with "error": "rate_limited" and retry_after_seconds |
Your account made 60 CORS and lifecycle rule changes in the last hour. The Retry-After header says how long to wait. |
502 |
The bucket would not take these CORS rules. Try again in a minute. |
The bucket's region refused the rules. Nothing changed. |
API operations on this page
| Method | Path | What it does |
|---|---|---|
GET | /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/cors | Get bucket cors |
PUT | /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/cors | Replace the bucket's CORS rules with the ones sent, at most 99 |
DELETE | /api/v1/client/object-storage/{service_id}/buckets/{bucket_id}/cors | Remove every CORS rule from the bucket |