How to add CORS to an S3 bucket in AWS CDK

avatar
Borislav Hadzhiev

Last updated: Jan 27, 2024
5 min

banner

# Table of Contents

  1. Setting up CORS for an S3 bucket in CDK
  2. Configure CORS for an AWS S3 Bucket

# Setting up CORS for an S3 bucket in CDK

CORS is a mechanism that allows a server to use a combination of HTTP headers to indicate from which domains, other than its own, it receives requests.

By default servers only take requests made from applications hosted on the same domain.

Setting CORS configuration is important when we access resources from a different domain.

All S3 buckets are hosted on the https://amazonaws.com domain, which is different from where we host our client applications, so we have to set up CORS.

We often have to set up CORS when working with S3 buckets, an example being when our backend generates an S3 presigned URL, which our frontend then uses to upload a file to S3.

Since our frontend is hosted on a different domain than the S3 bucket, we have to set up CORS.

In order to set CORS on an S3 bucket we have to pass the cors prop to the Bucket construct.

The code for this article is available on GitHub

To demo setting up CORS, let's create a CDK app that consists of a single S3 bucket.

lib/cdk-starter-stack.ts
import * as s3 from 'aws-cdk-lib/aws-s3'; import * as cdk from 'aws-cdk-lib'; export class MyCdkStack extends cdk.Stack { constructor(scope: cdk.App, id: string, props: cdk.StackProps) { super(scope, id, props); const s3Bucket = new s3.Bucket(this, id, { // 👇 Setting up CORS cors: [ { allowedMethods: [ s3.HttpMethods.GET, s3.HttpMethods.POST, s3.HttpMethods.PUT, ], allowedOrigins: ['http://localhost:3000'], allowedHeaders: ['*'], }, ], }); } } const app = new cdk.App(); new MyCdkStack(app, 'my-cdk-stack', { stackName: 'my-cdk-stack', env: { region: process.env.CDK_DEFAULT_REGION, account: process.env.CDK_DEFAULT_ACCOUNT, }, });
If you still use CDK version 1, switch to the cdk-v1 branch in the GitHub repository.

In the code sample, we:

  1. Used the Bucket construct to create an S3 bucket

  2. Set the CORS configuration of the bucket, where:

    The allowedMethods property is an array of the HTTP methods the domains in allowedOrigins are allowed to issue.

    The allowedOrigins property is an array of the domains that have permission to access the bucket. If you own a domain https://example.com, that's what you should specify here.

    The allowedHeaders property specifies the response headers we want to be able to access in our frontend code.

Depending on the HTTP methods your frontend needs to use when making requests to the bucket, you might have to tweak the options.

For example, if you're only making GET requests - just set GET as an allowed method.

Let's deploy the CDK stack and look at the results:

shell
npx aws-cdk deploy

Our CloudFormation stack has been provisioned successfully:

cloudformation stack bucket

If we open the S3 console and look at the CORS config in the Permissions section, we can see that CORS is successfully configured.

cors config bucket

# Troubleshooting CORS configuration of S3 Buckets

Note that we often get CORS errors when making HTTP requests to S3 buckets or APIs, when we type in the endpoint or the HTTP method wrong.

For instance, if I make a POST request to aws.amazon.com/wrong-path I'll get a CORS error in the console of my browser.

The same happens if I send the request with a wrong HTTP method that the endpoint does not support, I'll still get a CORS error.

Here is an example of configuring CORS outside of CDK.

# Configure CORS for an AWS S3 Bucket

CORS is a mechanism that allows a server to use a combination of HTTP headers to indicate from which domains, other than its own, it receives requests.

By default servers only take requests made from applications hosted on the same domain.

All S3 buckets are hosted on the https://amazonaws.com domain, which is different from where we host our client applications, so we have to set up CORS.

To configure an S3 bucket to allow cross-origin requests, you have to:

  1. Open your AWS S3 console and click on the bucket's name
  2. Click on the Permissions tab
  3. Scroll down to the Cross-origin resource sharing (CORS) section and click on the Edit button
  4. In the textarea field, enter your JSON CORS configuration
cors-configuration
[ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "GET", "HEAD", "PUT", "POST" ], "AllowedOrigins": [ "*" ], "MaxAgeSeconds": 3000 } ]

The CORS configuration consists of the following properties:

  • AllowedHeaders - which headers are allowed to be sent in a preflight (OPTIONS) request.

    In the example above we've used the * wildcard character to specify that all headers are allowed in a preflight request.

  • AllowedMethods - which HTTP methods the specified origins are allowed to perform on the S3 bucket.

    You might have to tweak this value, e.g. if your frontend only needs to make GET requests to the S3 bucket, you can remove the PUT and POST methods.

    Conversely, if you need to make DELETE requests to the S3 bucket, add the method.

  • AllowedOrigins - which origins (domains) are allowed to make requests to the S3 bucket.

    In our case we've set the value to a wildcard, meaning all domains. However, it's a best practice to only allow requests from known origins. If you own a domain https://example.com this is what you would specify here.

    You can pass multiple origins in the array, however, note that each origin can contain a maximum of 1 wildcard character. For example, specifying an origin of https://*.example.com, would enable requests to your S3 bucket from all subdomains, e.g. - https://wwww.example.com and https://blog.example.com.

  • MaxAgeSeconds - this is an optional element. It defines the time in seconds that the browser caches an S3 response to a preflight OPTIONS request for the specified resource.

    If the response is cached, the browser doesn't have to send OPTIONS requests for the same resource over and over.

We can provide multiple rule objects in a CORS configuration, in fact up to 100 rules can be defined in a single CORS configuration.

Let's look at an example with 2 rules. The configuration allows PUT, POST and DELETE requests from a specific origin and GET requests from all origins.

cors-multiple-rules
[ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "POST", "PUT", "DELETE" ], "AllowedOrigins": [ "https://www.example.com" ] }, { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "GET", "HEAD" ], "AllowedOrigins": [ "*" ], "MaxAgeSeconds": 3000 } ]

In the CORS configuration above, we've specified that all origins can make GET requests to our S3 bucket.

However, only the https://www.example.com origin can perform POST, PUT and DELETE operations on the bucket.

It's a best practice to configure minimal permissions. Only include the AllowedMethods and AllowedOrigins that your application needs to function properly.

I've also written an article on how to enable CORS for API Gateway or HTTP API in AWS CDK.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.