Adding CORS Headers Using Lambda@Edge and Amazon CloudFront

Cross-origin resource sharing (CORS) defines a way for client web applications that are loaded in one domain to interact with resources in a different domain, it allows to build rich client-side web applications and selectively allow cross-origin access to your resources. To see how to enable CORS on most web servers see https://enable-cors.org/index.html.

If you’re using Amazon CloudFront to serve your content, you will need to configure the distribution to respect the CORS settings by forwarding specific headers in the CloudFront behavior.

When HTTP headers are forwarded to the origin, CloudFront caches separate versions of a specified object based on the header values in viewer requests. It is a best practice to not forward requests to the origin if the requested object is already cached at the edge location, as this decreases the cache Hit ratio. Response bodies in requests that contain the Origin header are the same even if the values of the Origin header are different.

In this post we will leverage Lambda@Edge to set the CORS headers when the request contains the Origin header. With this solution, you don’t need to enable CORS at the origin or to forward the Origin Header in the CloudFront distribution.

Lambda@Edge provides the ability to execute a Lambda function at an Amazon CloudFront Edge Location. This capability enables intelligent processing of HTTP requests at locations that are close (for the purposes of latency) to your customers. To get started, you simply upload your code (Lambda function written in Node.js) and pick one of the CloudFront behaviours associated with your distribution.

You can run a Lambda@Edge function in response to four different CloudFront events:

      Viewer Request: When CloudFront receives a request from a viewer
      Origin Request: Before CloudFront forwards a request to the origin
      Origin Response: When CloudFront receives a response from the origin
      Viewer Response: Before CloudFront returns the response to the viewer

For more information about Lambda@Edge and how it works, see: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-at-the-edge.html.

For the purpose of this blog post, we’ll be focusing on the Viewer Response Event, I use the below Node.js code in the main index.js file. For step by step guide on how to create a Lambda function and associate it to a CloudFront behaviour see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-how-it-works-tutorial.html.

Viewer Response Code Snippet

'use strict';
exports.handler = (event, context, callback) => {

//Get contents of response
const response = event.Records[0].cf.response;
const headers = response.headers;

if ('origin' in event.Records[0].cf.request.headers) {
//The Request contains the Origin Header - Set CORS headers
headers['access-control-allow-origin'] = [{key: 'Access-Control-Allow-Origin', value: "*"}];
headers['access-control-allow-methods'] = [{key: 'Access-Control-Allow-Methods', value: "GET, HEAD"}];
headers['access-control-max-age'] = [{key: 'Access-Control-Max-Age', value: "86400"}];
}
//Return modified response
callback(null, response);
};

I’ve associated the above Node.js code to a CloudFront distribution behaviour (Viewer Response) that has as Origin an S3 bucket for which CORS is not enabled (note the CORS Headers and the Hit from CloudFront on the second request):

$ curl -v http://d2fyouwzorwsid.cloudfront.net/file.txt >/dev/null
> GET /file.txt HTTP/1.1
> Host: d2fyouwzorwsid.cloudfront.net
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Content-Length: 5
< Connection: keep-alive
< x-amz-id-2: JYE+LYMvI6Vmu13MCoPJZtpF76Ozs3W5AiY6KvwrG8t4KEq3/CcSE1WCR85ra6f9yxSh1zqGIi4=
< x-amz-request-id: 1FE8E25F4E96C9AF
< Date: Thu, 02 May 2019 19:15:34 GMT
< Last-Modified: Thu, 02 May 2019 19:13:46 GMT
< ETag: "2205e48de5f93c784733ffcca841d2b5"
< Cache-Control: max-age=500
< Accept-Ranges: bytes
< Server: AmazonS3
< X-Cache: Miss from cloudfront
< Via: 1.1 2e50d9b1ee017f302768660f02b7418e.cloudfront.net (CloudFront)
< X-Amz-Cf-Id: fWxXPrnynlKchjfD3gS3q4QxvAsPyVH1XEZRYKhR3pdO3NPlpE04Jg==

$ curl -v -H "Origin:www.example.com" http://d2fyouwzorwsid.cloudfront.net/file.txt >/dev/null
> GET /file.txt HTTP/1.1
> Host: d2fyouwzorwsid.cloudfront.net
> User-Agent: curl/7.54.0
> Accept: */*
> Origin:www.example.com
>
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Content-Length: 5
< Connection: keep-alive
< x-amz-id-2: JYE+LYMvI6Vmu13MCoPJZtpF76Ozs3W5AiY6KvwrG8t4KEq3/CcSE1WCR85ra6f9yxSh1zqGIi4=
< x-amz-request-id: 1FE8E25F4E96C9AF
< Date: Thu, 02 May 2019 19:15:34 GMT
< Last-Modified: Thu, 02 May 2019 19:13:46 GMT
< ETag: "2205e48de5f93c784733ffcca841d2b5"
< Cache-Control: max-age=500
< Accept-Ranges: bytes
< Server: AmazonS3
< Age: 11
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, HEAD
< Access-Control-Max-Age: 86400
< X-Cache: Hit from cloudfront
< Via: 1.1 1448f69604d5be1f9c9f0c64cfa90595.cloudfront.net (CloudFront)
< X-Amz-Cf-Id: CNvc-aOmxsrlAbogKUOYTBm-oOEV3VQjawQZ5LCj5CW1VYxK97a8-A==

Congratulations! You have successfully implemented cross-origin resource sharing without enabling CORS at the origin server or forwarding the Origin Header to the origin.

1 comment

  1. Wonderful, I was exploring the internet to meet my need. And now it`s quite fortunate that I came across this website. This is completely informative and lots of things that we can know from this. Looking at trending technologies and their usage determines the impact of turning the world beautiful.

Leave a Reply

Your email address will not be published.