How to add CORS to S3 with CloudFront

March 21, 2024
Sergio Rua

This is a requirement in one of the projects I’m working on. I thought this would be straightforward as there is plenty of documentation and examples. But alas, it led to a lot of head-banging and cursing. Why? Because the documentation is all wrong (or I can’t read, and that’s very possible)! AWS changed the Cloudfront payload format some time ago and the examples did not work for me.

Courtesy of our robot overlords

These two errors came up when I was trying the examples. I’m adding them here for SEO purposes so that people may find the answer.

The Lambda function result failed validation: The function tried to add, delete, or change a read-only header.

and

The Lambda function returned an invalid entry in the headers object: Each header entry in the headers object must be an array.

Show me how

My AWS lamdba is pretty simple. Note that the headers are not just an object but also an array. This was crucial.

import json

def lambda_handler(event, context):
    # Get the request from the CloudFront event
    response = event['Records'][0]['cf']['response']

    # Set the CORS headers
    response_headers = {
        'access-control-allow-origin': [
            {
                'value': '*'
            }
        ],
        'access-control-allow-methods': [
            {
                'value': 'GET'
            }
        ],
        'access-control-allow-headers': [
            {
                "value": 'content-type'
            }
        ],
    }

    response['headers'].update(response_headers)

    return response

If you’re using terraform, it’s very easy to deploy this function to AWS. The function must be deployed to us-east-1 and to the edge. This is my code.

provider "aws" {
  region = "us-east-1"
  alias  = "us"
}

module "cf_lambda" {
  providers = {
    aws = aws.us
  }
  source  = "terraform-aws-modules/lambda/aws"
  version = "6.5.0"

  function_name = "${var.env}-cloudfront-cors"
  description   = "Injects CORS to requests"
  handler       = "main.lambda_handler"
  runtime       = "python3.9"

  publish                           = true
  cloudwatch_logs_retention_in_days = 3
  lambda_at_edge                    = true

  create_package = true
  source_path    = "${path.module}/lambda"

  attach_policy_statements = true
  policy_statements = {
    lambda = {
      effect    = "Allow",
      actions   = ["lambda:GetFunction", "lambda:EnableReplication*", "lambda:DisableReplication*"],
      resources = ["*"]
    },
    cloudfront = {
      effect    = "Allow",
      actions   = ["cloudfront:CreateDistribution"],
      resources = ["*"]
    }
  }
}

output "lambda_function_arn" {
  value = "${module.cf_lambda.lambda_function_arn}:${module.cf_lambda.lambda_function_version}"
}

Cloudfront Config

Obviously, you should do this with Terraform or whichever automation tool you use but in broad strokes, this is what you need:

  • Create a Cloudfront distribution
  • Create a S3 bucket for the assets for which you want to add CORS
  • Add an Origin to the Cloudfront distribution of type s3 and point it to your bucket using the format
    BUCKET_NAME.s3.REGION.amazonaws.com
  • Under Behaviours, add a new entry and configure there the cache policy.
  • Finally, attach the Lambda function to the viewer-response. Please note that Cloudfront requires the lambda function ARN to point to a version and not just the main arn.

Final words

I was quite surprised that such a complex setup is required for something as simple as CORS. Even more so that it took me ages to configure it. Yes, I perhaps did not read the documentation or the examples properly but I found them confusing. I hope this helps somebody else.

As always, reach out to us if you need help.

I for one welcome our new overlords

Subscribe to newsletter

Subscribe to receive the latest blog posts to your inbox every week.

By subscribing you agree to with our Privacy Policy.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Ready to Transform 

Your Business?