Menu
Amazon CloudFront
Developer Guide (API Version 2016-09-29)

HTTP Response Generation

Lambda@Edge response generation enables Lambda functions that are associated with CloudFront viewer request events to create and return HTTP responses directly to clients. This effectively reduces latency and load on a CloudFront distribution’s origin. The HTTP response body can be created from scratch and response headers can be configured based on information available in the viewer request, from within your Lambda function.

Programming Model

This section provides details about the programming model for HTTP response generation using Lambda@Edge.

Potential Errors

The following are possible errors, their meaning, and how to avoid them.

HTTP HEAD Response Contains Body Field

Responses that are generated from an associated HTTP HEAD request should not contain a body field. If a body field is included in the generated response, the request will fail and display an HTTP 502 status code (Bad Gateway) to the viewer. To prevent this error, verify that the incoming HTTP request is a HEAD request and, if so, do not include a body field in the response.

Note that this follows RFC 2616, which states that the response to a HEAD request should not contain a body field.

HTTP No Content Response Contains Body Field

HTTP 204 status codes (No Content) responses that are generated from CloudFront viewer requests should not include a body field in the response. If a body field is included in the response, an HTTP 502 status code (Bad Gateway) is returned to the viewer.

This is because Lambda@Edge imposes the optional restriction found in RFC 2616, which states that an HTTP 204 response does not need to contain a body.

Body Size Limitation

Total response body size is limited to 40 KB. If the response is larger than this limit, an HTTP 502 status code (Bad Gateway) is returned to the viewer.

Whitelist and Blacklist Headers

There are no changes to the current whitelist or blacklist headers.

Required Fields

The following fields are required:

  • Status: If no status is present, it is treated like a request.

  • httpVersion: This must match the corresponding CloudFront distribution’s behavior. For example, if the request httpVersion is HTTP 1.1, then the response httpVersion must be HTTP 1.1.

All other fields are optional.

Use Case Examples

This section provides some examples of what you can build with Lambda@Edge response generation.

HTTP Redirects

You can generate HTTP redirect responses from Lambda functions that are associated with CloudFront viewer request events. These responses are not cached.

The following code examples show different HTTP redirect scenarios.

Redirect a Client Based on the User-Agent Header

You can use a Lambda function that is associated with a CloudFront viewer request event to return an HTTP redirect to viewers. This returned redirect is based on which device the client is using, for example, a mobile phone, computer, tablet, and so on:

Copy
'use strict'; exports.handler = (event, context, callback) => { /* * Extract request object in order to preserve httpVersion field. * This is necessary to match the client's httpVersion. Please * refer to your CloudFront Distribution's httpVersion configuration for whether to * specify HTTP 1.1, 2.0 or match-viewer. */ const request = event.Records[0].cf.request; let location = 'http://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html'; /* * Documentation provided by Mozilla (see below) suggests that existence of the * word "Mobi" or "Tablet" identifies a mobile device. * * https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent */ const headers = request.headers; if (headers['User-Agent']) { const userAgents = headers['User-Agent']; for (let i = 0; i < userAgents.length; i++) { const isMobileDevice = userAgents[i].search(/Mobi|Tablet/); if (isMobileDevice != -1) { location = 'http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-at-the-edge.html'; break; } } } /* * Generate HTTP redirect response using 302 status code. Location header * is required in order to invoke browser redirect responses. */ const response = { status: '302', statusDescription: '302 Found', httpVersion: request.httpVersion, headers: { Location: location, }, }; callback(null, response); };

Redirect to a New Domain Path Based on the Request URI

You can use a Lambda function to return an HTTP redirect to viewers based on the URI of the client request. Common use cases are AB testing, bulkheading, and general HTTP redirects:

Copy
'use strict'; exports.handler = (event, context, callback) => { /* * Extract request object in order to preserve httpVersion field. * This is necessary in order to match the client's httpVersion. Please * refer to your CloudFront Distribution's HttpVersion configuration for whether to * specify HTTP 1.1, 2.0 or match-viewer. */ const request = event.Records[0].cf.request; /* * Define a default redirect location if request.uri does not match any redirect paths. */ var location = 'https://aws.amazon.com/blogs/aws/'; /* * Define a map of redirect locations based on regular expression keys for paths that we care about. */ const redirects = { examplePath1: 'http://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html', examplePath2: 'http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-at-the-edge.html' }; /* * Search for known path requests that should be redirected. */ Object.keys(redirects).forEach(function(key) { if (request.uri.search(key) != -1) { location = redirects[key]; } }); /* * Generate HTTP redirect response using 302 status code. Location header * is required in order to invoke browser redirect responses. */ const response = { status: '302', statusDescription: '302 Found', httpVersion: request.httpVersion, headers: { Location: location, }, }; callback(null, response); };

Static Web Server

You can serve static website content from Lambda functions that are associated with a CloudFront viewer request event. To do this, you create path-specific cache behaviors for a CloudFront distribution and associate the appropriate Lambda functions to each behavior. Benefits to this approach include reducing the load on the origin server and reducing overall latency. Lambda@Edge limitations still apply, including body size limits, maximum duration, and so on. Not all use cases may be valid.

The following examples show how to define static content.

Static Content Returned to a Client

The following example shows static content returned to a client:

Copy
'use strict'; /* * Define static content returned to client */ let content = ' \ <\!DOCTYPE html> \ <html lang="en"> \ <head> \ <meta charset="utf-8"> \ <title>Simple Lambda@Edge Static Content Response</title> \ </head> \ <body> \ <!-- page content --> \ </body> \ </html> \ '; exports.handler = (event, context, callback) => { /* * Extract request object in order to preserve httpVersion field. * This is necessary to match the client's httpVersion. Please * refer to your CloudFront Distribution's httpVersion configuration for whether to * specify HTTP 1.1, 2.0 or match-viewer. */ const request = event.Records[0].cf.request; /* * Generate HTTP OK response using 200 status code with HTML body. * Lambda@Edge response bodies have size limits but no problem because * our response body size is small here. */ const response = { status: '200', statusDescription: 'HTTP OK', httpVersion: request.httpVersion, headers: { 'Cache-Control': ['max-age=100'], 'Content-Type': ['text/html'], 'Content-Encoding': ['UTF-8'] }, body: content, }; callback(null, response); };

HTTP Response with Compressed Static Content

The following example shows an HTTP response with compressed static content:

Copy
'use strict'; var zlib = require('zlib'); /* * Define static content returned to client */ let content = ' \ <!DOCTYPE html> \ <html lang="en"> \ <head> \ <meta charset="utf-8"> \ <title>Simple Lambda@Edge Compressed Static Content Response</title> \ </head> \ <body> \ <!-- page content --> \ </body> \ </html> \ '; exports.handler = (event, context, callback) => { /* * Extract request object in order to preserve httpVersion field. * This is necessary to match the client's httpVersion. Please * refer to your CloudFront Distribution's httpVersion configuration for whether to * specify HTTP 1.1, 2.0 or match-viewer. */ const request = event.Records[0].cf.request; /* * Here we compress the HTML content as an example of what we can do with Lambda@Edge */ var compressedContent = zlib.gzipSync(content); /* * Generate HTTP OK response using 200 status code with HTML body. * Lambda@Edge response bodies have size limits but no problem because * our response body size is small here. */ const response = { status: '200', statusDescription: 'HTTP OK', httpVersion: request.httpVersion, headers: { 'Cache-Control': ['max-age=100'], 'Content-Type': ['text/html; charset=UTF-8'], 'Content-Encoding': ['gzip'] }, body: compressedContent.toString('utf8'), }; callback(null, response); };

Authentication and Authorization

HTTP Authorization headers might be included in HTTP requests that are received by CloudFront. Lambda functions that are associated with CloudFront viewer request events are able to evaluate these headers and return an HTTP 403 status code (Forbidden), or an HTTP 401 status code (Unauthorized) if the client is unable to access the requested content. Otherwise, the CloudFront request is sent for further processing.

Authentication and Authorization

The following example shows how to evaluate HTTP authorization headers to perform authentication and authorization at edge locations:

Copy
'use strict'; exports.handler = function(event, context, callback) { /* * Extract request object in order to preserve httpVersion field. * This is necessary to match the client's httpVersion. Please * refer to your CloudFront Distribution's httpVersion configuration for whether to * specify HTTP 1.1, 2.0 or match-viewer. */ const request = event.Records[0].cf.request; /* * Authorization can take many forms. However, here we require an "Authorization" header to be sent * with the request. We can determine the authority of the caller by evaluating what is passed in. */ if (request.headers['Authorization']) { let authorized = false; const authorities = request.headers['Authorization']; for (let i = 0; i < authorities.length; i++) { if (authorities[i] === 'secret') { authorized = true; } } if (authorized) { callback(null, request); } else { callback(null, {status: '401', statusDescription: '401 Unauthorized', httpVersion: request.httpVersion}); } } else { // Client did not send authorization callback(null, {status: '401', statusDescription: '401 Unauthorized', httpVersion: request.httpVersion}); } };