Published 2021-03-21.
Time to read: 3 minutes.
This article shows how to enable CORS on an AWS S3 bucket with AWS CLI, then modify the bucket’s CloudFront distribution. In preparing this article, I found that the AWS S3 CORS documentation needs to be read in conjunction with how AWS CloudFront can be configured to handle CORS.
I used one origin for testing.
$ ORIGIN=ancientwarmth.com
$ JSON_FILE=cors.json
The CORS configuration for the AWS S3 bucket will be stored in the file pointed to by JSON_FILE
.
Define the AWS S3 Bucket CORS Configuration
This configuration (in JSON format) contains 1 rule:
- Allow
GET
HTTP methods from anywhere.
{ "CORSRules": [ { "AllowedHeaders": [], "AllowedMethods": [ "GET" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [] } ] }
You can read about CORS configuration in the AWS documentation.
Set the AWS S3 Bucket CORS Configuration
It is easy to set the CORS configuration for an AWS S3 bucket using AWS CLI’s
aws s3api put-bucket-cors
subcommand:
$ BUCKET=assets.ancientwarmth.com
$ aws s3api put-bucket-cors \ --bucket $BUCKET \ --cors-configuration "file://$JSON_FILE"
Test the AWS S3 Bucket CORS Configuration
Now it is time to test the S3 bucket’s CORS configuration using curl
.
I defined a bash function to peform the test to save typing.
You can use it by first copy/pasting the code below into a shell prompt,
then calling the function with the proper arguments, as shown.
The function requires 3 arguments: the request origin, the URL of an asset in an AWS S3 bucket, and an HTTP method (which must be in UPPPER CASE).
$ function testCors { if [ -z "$1" ]; then echo "Error: No origin was provided"; exit 1; fi if [ -z "$2" ]; then echo "Error: No URL to test was provided"; exit 1; fi if [ "$3" ]; then METHOD="$3"; else METHOD=GET; fi
curl -I -X OPTIONS \ --no-progress-meter \ -H "Origin: $1" \ -H "Access-Control-Request-Method: $METHOD" \ "$2" 2>&1 | \ grep --color=never 'Access-Control' }
The JSON file for testing CORS was
s3://$BUCKET/testCors.json
:
{ "key1": "value1", "key2": "value2" }
We will know if CORS is set up properly by receiving a header containing Access-Control-Allow-Origin: *
.
$ URL="https://s3.amazonaws.com/$BUCKET/testCors.json"
$ testCors $ORIGIN $URL GET Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET Vary: Origin, Access-Control-Request-Headers, Access-Control-Request-Method
The origin worked when the bucket is accessed via a GET
method sent to its s3.amazonaws.com
DNS alias (yay!).
CORScanner (discussed in a previous article) reported no issues:
$ cors -u s3.amazonaws.com/assets.ancientwarmth.com/testCors.json Starting CORS scan... Finished CORS scanning...
CloudFront
I have not worked through the process of using AWS CLI to obtain a JSON object describing the distribution,
and then changing some properties and writing it back.
So until that happy day comes, here are 2 screen shots of the
AWS CloudFront web console
showing the settings.
The first screen shot shows the Behaviors tab of the top-level details of the
assets.ancientwarmth.com
CloudFront distribution.
My application does not require users to upload anything, so everything in the S3 bucket is truly static.
Thus I have no need to PUT
, POST
or DELETE
HTTP methods for the AWS S3 content.
I have not seen a good explanation of why enabling OPTIONS
HTTP methods is necessary,
but every person on Stack Overflow who got CORS to work with AWS S3 says this was necessary.
With that in mind, I set the following for the next screen shot:
- Viewer Protocol Policy:
Redirect HTTP to HTTPS
- Allowed HTTP Methods:
GET, HEAD, OPTIONS
- Cached HTTP Methods: Enable
OPTIONS
- Use a cache policy and origin request policy: (default is Use legacy cache settings, which is usually undesirable)
- Cache Policy:
Managed-CachingOptimized
- Origin Request Policy:
Managed-CORS-S3Origin
Managed CORS S3 Origin Poligy
AWS CloudFront's managed origin request policy
called Managed-CORS-S3Origin
includes the headers that enable cross-origin resource sharing (CORS)
requests when the origin is an Amazon S3 bucket.
This policy's settings are:
- Query strings included in origin requests: None
- Headers included in origin requests:
Origin
Access-Control-Request-Headers
Access-Control-Request-Method
- Cookies included in origin requests: None
Wait or Invalidate
Whenever you make a configuration change to a CloudFront distribution, or the contents change, the distributed assets will not reflect those changes until the next CloudFront invalidation. Automatic invalidations take 20 minutes. You can invalidate manually for near-instant gratification. I use my AWS command-line utilities to invalidate manually:
$ awsCfS3DistId $BUCKET | awsCfInvalidate
Now the grand finale:
$ testCors $ORIGIN $URL GET Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET Vary: Origin, Access-Control-Request-Headers, Access-Control-Request-Method
The presence of the Access-Control-Allow-Origin
header indicates that CORS allowed the data file
to be transferred from the content server (AWS S3/CloudFront) to the origin server (the command line).