Skip to content

Cross-Origin Resource Sharing (CORS)

Estimated time to read: 3 minutes

Cross-Origin Resource Sharing (CORS) allows web applications running in one domain to access resources in a different domain. By default, web browsers block cross-origin requests for security reasons, but CORS lets you define exceptions to allow controlled cross-origin access to your buckets.

Access Control Bucket and Object ACL Bucket Policies

Enable CORS on a bucket

To enable CORS, you must define a CORS configuration for your bucket using the put-bucket-cors command.

Example: Allow https://example.com to perform GET and PUT requests:

aws s3api put-bucket-cors --bucket <bucket_name> --cors-configuration '{
    "CORSRules": [
        {
            "AllowedOrigins": ["https://example.com"],
            "AllowedMethods": ["GET", "PUT"],
            "AllowedHeaders": ["*"],
            "ExposeHeaders": ["ETag"],
            "MaxAgeSeconds": 3000
        }
    ]
}'

Create a new Python file named add-cors-configuration.py with the following content:

#!/usr/bin/env python3
import boto3

# Define the configuration rules
cors_configuration = {
    'CORSRules': [{
        'AllowedOrigins': ['https://example.com'],
        'AllowedMethods': ['GET', 'PUT'],
        'AllowedOrigins': ['*'],
        'ExposeHeaders': ['ETag'],
        'MaxAgeSeconds': 3000
    }]
}

# Set the CORS configuration
s3 = boto3.client('s3')
s3.put_bucket_cors(
    Bucket='<bucket_name>',
    CORSConfiguration=cors_configuration
)

Install required Python packages and run the script to set CORS configuration:

% pip install --user boto3
% python3 ./add-cors-configuration.py

This allows CORS requests from https://example.com on GET and PUT operations.

Get CORS from a bucket

Get the active CORS settings from a bucket:

aws s3api get-bucket-cors --bucket <bucket_name>
---
{
    "CORSRules": [
        {
            "AllowedOrigins": ["https://example.com"],
            "AllowedMethods": ["GET", "PUT"],
            "AllowedHeaders": ["*"],
            "ExposeHeaders": ["ETag"],
            "MaxAgeSeconds": 3000
        }
    ]
}

Create a new Python file named get-cors-configuration.py with the following content:

#!/usr/bin/env python3
import logging
import boto3
import json
from botocore.exceptions import ClientError


def get_bucket_cors(bucket_name):
    # Retrieve the CORS configuration
    s3 = boto3.client('s3')
    try:
        response = s3.get_bucket_cors(Bucket=bucket_name)
    except ClientError as e:
        if e.response['Error']['Code'] == 'NoSuchCORSConfiguration':
            return []
        else:
            # AllAccessDisabled error == bucket not found
            logging.error(e)
            return None
    return response['CORSRules']

print(json.dumps(get_bucket_cors(<bucket_name>), indent=2))

Install required Python packages and run the script to get CORS configuration:

% pip install --user boto3
% python3 get-cors-configuration.py
[
    {
        "AllowedHeaders": ["*"],
        "AllowedMethods": ["GET", "PUT"],
        "AllowedOrigins": ["https://example.com"],
        "ExposeHeaders": ["ETag"],
        "MaxAgeSeconds": 3000
    }
]

Delete CORS from a bucket

Remove the CORS settings from a bucket:

aws s3api delete-bucket-cors --bucket <bucket_name>

Create a new Python file named delete-cors-configuration.py with the following content:

#!/usr/bin/env python3
import boto3

# Delete the CORS configuration
s3 = boto3.client('s3')
s3.delete_bucket_cors(
    Bucket='<bucket_name>'
)

Install required Python packages and run the script to delete CORS configuration:

% pip install --user boto3
% python3 ./delete-cors-configuration.py

Verify your CORS configuration

The request below is made without authentication. For this to work, without authentication, enable a public bucket policy.

Execute the following CURL command to see it working:

curl -v -X OPTIONS -H "Origin: https://example.com" \
     -H "Access-Control-Request-Method: GET" \
     <object_store_url>/<tenant>/<bucket_name>/<object_name>
*   Trying <redacted_ip>:<redacted_port>...
* Connected to <redacted_ip> (<redacted_ip>) port <redacted_port>
> OPTIONS /<tenant>/<bucket_name>/<object_name> HTTP/1.1
> Host: <redacted_ip>:<redacted_port>
> Accept: */*
> Origin: https://example.com
> Access-Control-Request-Method: PUT
>
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: https://example.com
< Vary: Origin
< Access-Control-Allow-Methods: PUT
< Access-Control-Expose-Headers: ETag
< Access-Control-Max-Age: 3000
<
* Connection #0 to host <redacted_ip> left intact

In the example you can find that the returning response now includes: Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Expose-Headers and Access-Control-Max-Age.