How to configure a CORS policy on a bucket

Last updated on: October 28, 2026

Web browsers stop JavaScript on one site from using files on another site, unless the other site allows it. The way a site allows it is called Cross-Origin Resource Sharing (CORS). A bucket allows nothing by default. A web page at https://www.example.com that loads files from your bucket with JavaScript therefore fails with a CORS error in the browser.

To allow it, you add a CORS policy to the bucket. The policy lists the sites, called origins, that can send requests to the bucket, and the kinds of requests they can send. Images and videos that a page shows with ordinary HTML tags don't need a CORS policy. You only need one when the page's JavaScript downloads or uploads files.

This guide walks you through adding a CORS policy with the AWS CLI, checking it and removing it.

Before you start

You need:

A CORS policy doesn't give anyone access to your files. The browser's requests still need permission of their own. Either the files are in a public read bucket, as in How to set up a public read bucket, or the page uses links that carry their own permission, as in How to perform basic Object Storage operations using AWS CLI.

Write the CORS policy

A CORS policy is a JSON document with one or more rules. This one lets JavaScript on https://www.example.com download files from the bucket:

{
  "CORSRules": [
    {
      "AllowedOrigins": ["https://www.example.com"],
      "AllowedMethods": ["GET", "HEAD"],
      "AllowedHeaders": ["*"],
      "ExposeHeaders": ["ETag"],
      "MaxAgeSeconds": 3000
    }
  ]
}

Each field sets one part of the rule:

  • AllowedOrigins lists the sites that can send requests. An origin is the scheme and host name, such as https://www.example.com, with no path or trailing slash. To allow several sites, list them all, for example ["https://www.example.com", "https://admin.example.com"].
  • AllowedMethods lists the kinds of requests that the sites can send. GET and HEAD are for downloading. To let the page upload files too, add PUT.
  • AllowedHeaders lists the request headers that the page can send. * allows any header.
  • ExposeHeaders lists the response headers that the page's JavaScript can read, here the file's ETag.
  • MaxAgeSeconds sets how long the browser can remember the bucket's answer before it asks again.

Save the policy as cors.json, with your own site in AllowedOrigins.

Use the exact origins of your own sites. "AllowedOrigins": ["*"] allows every site on the internet, so use it only for files that any site may use.

Add the policy to the bucket

aws s3api put-bucket-cors --bucket <bucket-name> --cors-configuration file://cors.json --profile upcloud

The command prints nothing when it succeeds. A bucket has one CORS policy, so this replaces any policy that the bucket already has.

To see the bucket's policy, run:

aws s3api get-bucket-cors --bucket <bucket-name> --profile upcloud
Output
{
    "CORSRules": [
        {
            "AllowedHeaders": [
                "*"
            ],
            "AllowedMethods": [
                "GET",
                "HEAD"
            ],
            "AllowedOrigins": [
                "https://www.example.com"
            ],
            "ExposeHeaders": [
                "ETag"
            ],
            "MaxAgeSeconds": 3000
        }
    ]
}

Check the policy

Before a browser sends a request to another site, it asks that site whether the request is allowed. This first request is called a preflight request. To check the policy without a browser, send the same preflight request with curl. Use the URL of any file in the bucket and one of the origins in the policy:

curl -i -X OPTIONS https://<endpoint>/<bucket-name>/<file-path> -H "Origin: https://www.example.com" -H "Access-Control-Request-Method: GET"

<endpoint> is the host name of the service's S3 endpoint, for example abcd1.upcloudobjects.com. You can find it on the service's Overview tab, under Public access.

Output
HTTP/2 200
date: Wed, 28 Oct 2026 06:57:29 GMT
server: ViPR/1.0
...
access-control-allow-origin: https://www.example.com
access-control-expose-headers: ETag
access-control-allow-methods: GET, HEAD
access-control-max-age: 3000
content-length: 0

The access-control-allow-origin header names your site, so a browser on that site goes ahead with the request. For a site that isn't in AllowedOrigins, or a method that isn't in AllowedMethods, the answer is HTTP/2 403 with an InvalidCorsRequest error and no access-control headers, so the browser blocks the request.

Remove the policy

aws s3api delete-bucket-cors --bucket <bucket-name> --profile upcloud

The command prints nothing when it succeeds. Browsers then block JavaScript requests from other sites again.

Next steps

Contributed by: Samir Haliru

Can't find what you're looking for?

For more help you can contact our awesome 24/7 support team