

# Customize at the edge with Lambda@Edge
<a name="lambda-at-the-edge"></a>

Lambda@Edge is an extension of AWS Lambda. Lambda@Edge is a compute service that lets you execute functions that customize the content that Amazon CloudFront delivers. You can author Node.js or Python functions in the Lambda console in one AWS Region, US East (N. Virginia).

After you create the function, you can add triggers using the Lambda console or CloudFront console so that the functions run in AWS locations that are closer to the viewer, without provisioning or managing servers. Optionally, you can use Lambda and CloudFront API operations to set up your functions and triggers programmatically.

Lambda@Edge scales automatically, from a few requests per day to thousands per second. Processing requests at AWS locations closer to the viewer instead of on origin servers significantly reduces latency and improves the user experience.

**Note**  
Lambda@Edge isn't supported with gRPC requests. For more information. see [Using gRPC with CloudFront distributions](distribution-using-grpc.md).

**Topics**
+ [How Lambda@Edge works with requests and responses](lambda-edge-event-request-response.md)
+ [Ways to use Lambda@Edge](lambda-edge-ways-to-use.md)
+ [Get started with Lambda@Edge functions (console)](lambda-edge-how-it-works.md)
+ [Set up IAM permissions and roles for Lambda@Edge](lambda-edge-permissions.md)
+ [Write and create a Lambda@Edge function](lambda-edge-create-function.md)
+ [Add triggers for a Lambda@Edge function](lambda-edge-add-triggers.md)
+ [Test and debug Lambda@Edge functions](lambda-edge-testing-debugging.md)
+ [Delete Lambda@Edge functions and replicas](lambda-edge-delete-replicas.md)
+ [Lambda@Edge event structure](lambda-event-structure.md)
+ [Work with requests and responses](lambda-generating-http-responses.md)
+ [Lambda@Edge example functions](lambda-examples.md)

# How Lambda@Edge works with requests and responses
<a name="lambda-edge-event-request-response"></a>

When you associate a CloudFront distribution with a Lambda@Edge function, CloudFront intercepts requests and responses at CloudFront edge locations. You can execute Lambda functions when the following CloudFront events occur:
+ When CloudFront receives a request from a viewer (viewer request)
+ Before CloudFront forwards a request to the origin (origin request)
+ When CloudFront receives a response from the origin (origin response)
+ Before CloudFront returns the response to the viewer (viewer response)

If you're using AWS WAF, the Lambda@Edge viewer request is executed after any AWS WAF rules are applied.

For more information, see [Work with requests and responses](lambda-generating-http-responses.md) and [Lambda@Edge event structure](lambda-event-structure.md).

# Ways to use Lambda@Edge
<a name="lambda-edge-ways-to-use"></a>

There are many uses for Lambda@Edge processing with your Amazon CloudFront distribution, such as the following examples:
+ A Lambda function can inspect cookies and rewrite URLs so that users see different versions of a site for A/B testing.
+ CloudFront can return different objects to viewers based on the device they're using by checking the `User-Agent` header, which includes information about the devices. For example, CloudFront can return different images based on the screen size of their device. Similarly, the function could consider the value of the `Referer` header and cause CloudFront to return the images to bots that have the lowest available resolution. 
+ Or you could check cookies for other criteria. For example, on a retail website that sells clothing, if you use cookies to indicate which color a user chose for a jacket, a Lambda function can change the request so that CloudFront returns the image of a jacket in the selected color.
+ A Lambda function can generate HTTP responses when CloudFront viewer request or origin request events occur.
+ A function can inspect headers or authorization tokens, and insert a header to control access to your content before CloudFront forwards the request to your origin.
+ A Lambda function can also make network calls to external resources to confirm user credentials, or fetch additional content to customize a response.

For more information, including example code, see [Lambda@Edge example functions](lambda-examples.md).

For more information about setting up Lambda@Edge in the console, see [Tutorial: Create a basic Lambda@Edge function (console)](lambda-edge-how-it-works-tutorial.md).

# Get started with Lambda@Edge functions (console)
<a name="lambda-edge-how-it-works"></a>

With Lambda@Edge, you can use CloudFront triggers to invoke a Lambda function. When you associate a CloudFront distribution with a Lambda function, CloudFront [intercepts requests and responses](https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html) at CloudFront edge locations and runs the function. Lambda functions can improve security or customize information close to your viewers to improve performance.

The following list provides a basic overview of how to create and use Lambda functions with CloudFront.

**Overview: Creating and using Lambda functions with CloudFront**

1. Create a Lambda function in the US East (N. Virginia) Region.

1. Save and publish a numbered version of the function.

   If you want to change the function, you must edit the \$1LATEST version of the function in the US East (N. Virginia) Region. Then, before you set it up to work with CloudFront, you publish a new numbered version.

1. Associate the function with a CloudFront distribution and cache behavior. Then specify one or more CloudFront events (*triggers*) that cause the function to execute. For example, you can create a trigger for the function to execute when CloudFront receives a request from a viewer.

1. When you create a trigger, Lambda creates replicas of the function at AWS locations around the world.

**Tip**  
For more information, see [creating and updating functions](lambda-edge-create-function.md), [the event structure](lambda-event-structure.md), and [adding CloudFront triggers](lambda-edge-add-triggers.md). You can also find more ideas and get code samples in [Lambda@Edge example functions](lambda-examples.md).

For a step-by-step tutorial, see the following topic:

**Topics**
+ [Tutorial: Create a basic Lambda@Edge function (console)](lambda-edge-how-it-works-tutorial.md)

# Tutorial: Create a basic Lambda@Edge function (console)
<a name="lambda-edge-how-it-works-tutorial"></a>

This tutorial shows you how to get started with Lambda@Edge by creating and configuring an example Node.js function that runs in CloudFront. This example adds HTTP security headers to a response when CloudFront retrieves a file. (This can improve security and privacy for a website.)

You don't need your own website for this tutorial. However, when you choose to create your own Lambda@Edge solution, you follow similar steps and select from the same options.

**Topics**
+ [Step 1: Sign up for an AWS account](#lambda-edge-how-it-works-tutorial-AWS)
+ [Step 2: Create a CloudFront distribution](#lambda-edge-how-it-works-tutorial-cloudfront)
+ [Step 3: Create your function](#lambda-edge-how-it-works-tutorial-create-function)
+ [Step 4: Add a CloudFront trigger to run the function](#lambda-edge-how-it-works-tutorial-add-trigger)
+ [Step 5: Verify that the function runs](#lambda-edge-how-it-works-tutorial-verify)
+ [Step 6: Troubleshoot issues](#lambda-edge-how-it-works-tutorial-troubleshoot)
+ [Step 7: Clean up your example resources](#lambda-edge-how-it-works-tutorial-cleanup-resources)
+ [Related information](#lambda-edge-how-it-works-tutorial-resources)

## Step 1: Sign up for an AWS account
<a name="lambda-edge-how-it-works-tutorial-AWS"></a>

If you haven't already done so, sign up for an AWS account. For more information, see [Sign up for an AWS account](setting-up-cloudfront.md#sign-up-for-aws).

## Step 2: Create a CloudFront distribution
<a name="lambda-edge-how-it-works-tutorial-cloudfront"></a>

Before you create the example Lambda@Edge function, you must have a CloudFront environment to work with that includes an origin to serve content from.

For this example, you create a CloudFront distribution that uses an Amazon S3 bucket as the origin for the distribution. If you already have an environment to use, you can skip this step.<a name="lambda-edge-how-it-works-tutorial-cf-proc"></a>

**To create a CloudFront distribution with an Amazon S3 origin**

1. Create an Amazon S3 bucket with a file or two, such as image files, for sample content. For help, follow the steps in [Upload your content to Amazon S3](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.html#GettingStartedUploadContent). Make sure that you set permissions to grant public read access to the objects in your bucket.

1. Create a CloudFront distribution and add your S3 bucket as an origin, by following the steps in [Create a CloudFront web distribution](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/GettingStarted.html#GettingStartedCreateDistribution). If you already have a distribution, you can add the bucket as an origin for that distribution instead.
**Tip**  
Make a note of your distribution ID. Later in this tutorial when you add a CloudFront trigger for your function, you must choose the ID for your distribution in a dropdown list—for example, `E653W22221KDDL`.

## Step 3: Create your function
<a name="lambda-edge-how-it-works-tutorial-create-function"></a>

In this step, you create a Lambda function from a blueprint template in the Lambda console. The function adds code to update security headers in your CloudFront distribution. <a name="lambda-edge-how-it-works-tutorial-create-function-blueprint-proc"></a>

**To create a Lambda function**

1. Sign in to the AWS Management Console and open the AWS Lambda console at [https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/).
**Important**  
Make sure that you're in the **US-East-1 (N. Virginia)** AWS Region (**us-east-1**). You must be in this Region to create Lambda@Edge functions.

1. Choose **Create function**.

1. On the **Create function** page, choose **Use a blueprint**, and then filter for the CloudFront blueprints by entering **cloudfront** in the search field.
**Note**  
CloudFront blueprints are available only in the **US-East-1 (N. Virginia)** Region (**us-east-1**).

1. Choose the **Modify HTTP response header** blueprint as the template for your function.

1. Enter the following information about your function:
   + **Function name** – Enter a name for your function.
   + **Execution role** – Choose how to set the permissions for your function. To use the recommended basic Lambda@Edge permissions policy template, choose **Create a new role from AWS policy templates**.
   + **Role name** – Enter a name for the role that the policy template creates.
   + **Policy templates** – Lambda automatically adds the policy template **Basic Lambda@Edge permissions** because you chose a CloudFront blueprint as the basis for your function. This policy template adds execution role permissions that allow CloudFront to run your Lambda function for you in CloudFront locations around the world. For more information, see [Set up IAM permissions and roles for Lambda@Edge](lambda-edge-permissions.md).

1. Choose **Create function** at the bottom of the page.

1. In the **Deploy to Lambda@Edge** pane that appears, choose **Cancel**. (For this tutorial, you must modify the function code before deploying the function to Lambda@Edge.)

1. Scroll down to the **Code source** section of the page.

1. Replace the template code with a function that modifies the security headers that your origin returns. For example, you could use code similar to the following:

   ```
   'use strict';
   export const handler = (event, context, callback) => {
   
       //Get contents of response
       const response = event.Records[0].cf.response;
       const headers = response.headers;
   
       //Set new headers
       headers['strict-transport-security'] = [{key: 'Strict-Transport-Security', value: 'max-age= 63072000; includeSubdomains; preload'}];
       headers['content-security-policy'] = [{key: 'Content-Security-Policy', value: "default-src 'none'; img-src 'self'; script-src 'self'; style-src 'self'; object-src 'none'"}];
       headers['x-content-type-options'] = [{key: 'X-Content-Type-Options', value: 'nosniff'}];
       headers['x-frame-options'] = [{key: 'X-Frame-Options', value: 'DENY'}];
       headers['x-xss-protection'] = [{key: 'X-XSS-Protection', value: '1; mode=block'}];
       headers['referrer-policy'] = [{key: 'Referrer-Policy', value: 'same-origin'}];
   
       //Return modified response
       callback(null, response);
   };
   ```

1. Choose **File**, **Save** to save your updated code.

1. Choose **Deploy**.

Proceed to the next section to add a CloudFront trigger to run the function.

## Step 4: Add a CloudFront trigger to run the function
<a name="lambda-edge-how-it-works-tutorial-add-trigger"></a>

Now that you have a Lambda function to update security headers, configure the CloudFront trigger to run your function to add the headers in any response that CloudFront receives from the origin for your distribution.<a name="lambda-edge-how-it-works-tutorial-add-trigger-proc"></a>

**To configure the CloudFront trigger for your function**

1. In the Lambda console, on the **Function overview** page for your function, choose **Add trigger**.

1. For **Trigger configuration**, choose **CloudFront**.

1. Choose **Deploy to Lambda@Edge**.

1. In the **Deploy to Lambda@Edge** pane, under **Configure CloudFront trigger**, enter the following information:
   + **Distribution** – The CloudFront distribution ID to associate with your function. In the dropdown list, choose the distribution ID.
   + **Cache behavior** – The cache behavior to use with the trigger. For this example, leave the value set to **\$1**, which means your distribution's default cache behavior. For more information, see [Cache behavior settings](DownloadDistValuesCacheBehavior.md) in the [All distribution settings reference](distribution-web-values-specify.md) topic.
   + **CloudFront event** – The trigger that specifies when your function runs. We want the security headers function to run whenever CloudFront returns a response from the origin. In the dropdown list, choose **Origin response**. For more information, see [Add triggers for a Lambda@Edge function](lambda-edge-add-triggers.md).

1. Select the **Confirm deploy to Lambda@Edge** check box.

1. Choose **Deploy** to add the trigger and replicate the function to AWS locations worldwide.

1. Wait for the function to replicate. This typically takes several minutes.

    You can check to see if replication is finished by [going to the CloudFront console](https://console.aws.amazon.com/cloudfront/v4/home) and viewing your distribution. Wait for the distribution status to change from **Deploying** to a date and time, which means that your function has been replicated. To verify that the function works, follow the steps in the next section.

## Step 5: Verify that the function runs
<a name="lambda-edge-how-it-works-tutorial-verify"></a>

Now that you've created your Lambda function and configured a trigger to run it for a CloudFront distribution, check to make sure that the function is accomplishing what you expect it to. In this example, we check the HTTP headers that CloudFront returns, to make sure that the security headers are added.<a name="lambda-edge-how-it-works-tutorial-verify-proc"></a>

**To verify that your Lambda@Edge function adds security headers**

1. In a browser, enter the URL for a file in your S3 bucket. For example, you might use a URL similar to `https://d111111abcdef8.cloudfront.net/image.jpg`.

   For more information about the CloudFront domain name to use in the file URL, see [Customize the URL format for files in CloudFront](LinkFormat.md).

1. Open your browser's Web Developer toolbar. For example, in your browser window in Chrome, open the context (right-click) menu, and then choose **Inspect**.

1. Choose the **Network** tab.

1. Reload the page to view your image, and then choose an HTTP request on the left pane. You see the HTTP headers displayed in a separate pane.

1. Look through the list of HTTP headers to verify that the expected security headers are included in the list. For example, you might see headers similar to those shown in the following screenshot.  
![\[HTTP headers list with the expected security headers highlighted.\]](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/images/lambda-at-edge-security-headers-list.png)

If the security headers are included in your headers list, great\$1 You've successfully created your first Lambda@Edge function. If CloudFront returns errors or there are other issues, continue to the next step to troubleshoot the issues.

## Step 6: Troubleshoot issues
<a name="lambda-edge-how-it-works-tutorial-troubleshoot"></a>

If CloudFront returns errors or doesn't add the security headers as expected, you can investigate your function's execution by looking at CloudWatch Logs. Be sure to use the logs stored in the AWS location that is closest to the location where the function is executed.

For example, if you view the file from London, try changing the Region in the CloudWatch console to Europe (London).<a name="lambda-edge-how-it-works-tutorial-cloudwatch-proc"></a>

**To examine CloudWatch logs for your Lambda@Edge function**

1. Sign in to the AWS Management Console and open the CloudWatch console at [https://console.aws.amazon.com/cloudwatch/](https://console.aws.amazon.com/cloudwatch/).

1. Change **Region** to the location that is shown when you view the file in your browser. This is where the function is executing.

1. In the left pane, choose **Logs** to view the logs for your distribution. 

For more information, see [Monitor CloudFront metrics with Amazon CloudWatch](monitoring-using-cloudwatch.md).

## Step 7: Clean up your example resources
<a name="lambda-edge-how-it-works-tutorial-cleanup-resources"></a>

If you created an Amazon S3 bucket and CloudFront distribution just for this tutorial, delete the AWS resources that you allocated so that you no longer accrue charges. After you delete your AWS resources, any content that you added is no longer available.

**Tasks**
+ [Delete the S3 bucket](#lambda-edge-how-it-works-tutorial-delete-bucket) 
+ [Delete the Lambda function](#lambda-edge-how-it-works-tutorial-delete-function)
+ [Delete the CloudFront distribution](#lambda-edge-how-it-works-tutorial-delete-distribution)

### Delete the S3 bucket
<a name="lambda-edge-how-it-works-tutorial-delete-bucket"></a>

Before you delete your Amazon S3 bucket, make sure that logging is disabled for the bucket. Otherwise, AWS continues to write logs to your bucket as you delete it.<a name="lambda-edge-how-it-works-tutorial-delete-bucket-proc"></a>

**To disable logging for a bucket**

1. Open the Amazon S3 console at [https://console.aws.amazon.com/s3/](https://console.aws.amazon.com/s3/).

1. Select your bucket, and then choose **Properties**.

1. From **Properties**, choose **Logging**.

1. Clear the **Enabled** check box.

1. Choose **Save**.

Now, you can delete your bucket. For more information, see [Deleting a bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/delete-bucket.html) in the *Amazon Simple Storage Service Console User Guide*.

### Delete the Lambda function
<a name="lambda-edge-how-it-works-tutorial-delete-function"></a>

For instructions to delete the Lambda function association and optionally the function itself, see [Delete Lambda@Edge functions and replicas](lambda-edge-delete-replicas.md).

### Delete the CloudFront distribution
<a name="lambda-edge-how-it-works-tutorial-delete-distribution"></a>

Before you delete a CloudFront distribution, you must disable it. A disabled distribution is no longer functional and does not accrue charges. You can enable a disabled distribution at any time. After you delete a disabled distribution, it's no longer available.<a name="lambda-edge-how-it-works-tutorial-delete-distribution-proc"></a>

**To disable and delete a CloudFront distribution**

1. Open the CloudFront console at [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home).

1. Select the distribution that you want to disable, and then choose **Disable**.

1. When prompted for confirmation, choose **Yes, Disable**.

1. Select the disabled distribution, and then choose **Delete**.

1. When prompted for confirmation, choose **Yes, Delete**.

## Related information
<a name="lambda-edge-how-it-works-tutorial-resources"></a>

Now that you have a basic idea of how Lambda@Edge functions work, learn more by reading the following:
+ [Lambda@Edge example functions](lambda-examples.md)
+ [Lambda@Edge Design Best Practices](https://aws.amazon.com/blogs/networking-and-content-delivery/lambdaedge-design-best-practices/)
+ [Reducing Latency and Shifting Compute to the Edge with Lambda@Edge](https://aws.amazon.com/blogs/networking-and-content-delivery/reducing-latency-and-shifting-compute-to-the-edge-with-lambdaedge/)

# Set up IAM permissions and roles for Lambda@Edge
<a name="lambda-edge-permissions"></a>

To configure Lambda@Edge, you must have the following IAM permissions and roles for AWS Lambda: 
+ [IAM permissions ](#lambda-edge-permissions-required) – These permissions allow you to create your Lambda function and associate it with your CloudFront distribution.
+ [A Lambda function execution role ](#lambda-edge-permissions-function-execution) (IAM role) – The Lambda service principals assume this role to execute your function.
+ [Service-linked roles for Lambda@Edge](#using-service-linked-roles-lambda-edge) – The service-linked roles allow specific AWS services to replicate Lambda functions to AWS Regions and to enable CloudWatch to use CloudFront log files.

## IAM permissions required to associate Lambda@Edge functions with CloudFront distributions
<a name="lambda-edge-permissions-required"></a>

In addition to the IAM permissions that you need for Lambda, you need the following permissions to associate Lambda functions with CloudFront distributions:
+ `lambda:GetFunction` – Grants permission to get configuration information for your Lambda function and a presigned URL to download a `.zip` file that contains the function.
+ `lambda:EnableReplication*` – Grants permission to the resource policy so that the Lambda replication service can get the function code and configuration.
+ `lambda:DisableReplication*` – Grants permission to the resource policy so that the Lambda replication service can delete the function.
**Important**  
You must add the asterisk (`*`) at the end of the `lambda:EnableReplication*` and `lambda:DisableReplication*` actions.
+ For the resource, specify the ARN of the function version that you want to execute when a CloudFront event occurs, such as the following example:

  `arn:aws:lambda:us-east-1:123456789012:function:TestFunction:2`
+ `iam:CreateServiceLinkedRole` – Grants permission to create a service-linked role that Lambda@Edge uses to replicate Lambda functions in CloudFront. After you configure Lambda@Edge for the first time, the service-linked role is automatically created for you. You don't need to add this permission to other distributions that use Lambda@Edge.

  
+ `cloudfront:UpdateDistribution` or `cloudfront:CreateDistribution` – Grants permission to update or create a distribution.

For more information, see the following topics:
+ [Identity and Access Management for Amazon CloudFront](security-iam.md)
+ [Lambda resource access permissions](https://docs.aws.amazon.com/lambda/latest/dg/intro-permission-model.html#lambda-intro-execution-role) in the *AWS Lambda Developer Guide*

## Function execution role for service principals
<a name="lambda-edge-permissions-function-execution"></a>

You must create an IAM role that the `lambda.amazonaws.com` and `edgelambda.amazonaws.com` service principals can assume when they execute your function. 

**Tip**  
When you create your function in the Lambda console, you can choose to create a new execution role by using an AWS policy template. This step *automatically* adds the required Lambda@Edge permissions to execute your function. See [Step 5 in the Tutorial: Creating a simple Lambda@Edge function](lambda-edge-how-it-works-tutorial.md#lambda-edge-how-it-works-tutorial-create-function).

For more information about creating an IAM role manually, see [Creating roles and attaching policies (console)](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions_create-policies.html) in the *IAM User Guide*.

**Example: Role trust policy**  
You can add this role under the **Trust Relationship** tab in the IAM console. Don't add this policy under the **Permissions** tab.    
****  

```
{
   "Version":"2012-10-17",		 	 	 
   "Statement": [
      {
         "Effect": "Allow",
         "Principal": {
            "Service": [
               "lambda.amazonaws.com",
               "edgelambda.amazonaws.com"
            ]
         },
         "Action": "sts:AssumeRole"
      }
   ]
}
```

For more information about the permissions that you need to grant to the execution role, see [Lambda resource access permissions](https://docs.aws.amazon.com/lambda/latest/dg/intro-permission-model.html#lambda-intro-execution-role) in the *AWS Lambda Developer Guide*.

**Notes**  
By default, whenever a CloudFront event triggers a Lambda function, data is written to CloudWatch Logs. If you want to use these logs, the execution role needs permission to write data to CloudWatch Logs. You can use the predefined AWSLambdaBasicExecutionRole to grant permission to the execution role.  
For more information about CloudWatch Logs, see [Edge function logs](edge-functions-logs.md).
If your Lambda function code accesses other AWS resources, such as reading an object from an S3 bucket, the execution role needs permission to perform that action. 

## Service-linked roles for Lambda@Edge
<a name="using-service-linked-roles-lambda-edge"></a>

Lambda@Edge uses IAM [service-linked roles](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_terms-and-concepts.html#iam-term-service-linked-role). A service-linked role is a unique type of IAM role that is linked directly to a service. Service-linked roles are predefined by the service and include all of the permissions that the service requires to call other AWS services on your behalf.

Lambda@Edge uses the following IAM service-linked roles:
+ **AWSServiceRoleForLambdaReplicator** – Lambda@Edge uses this role to allow Lambda@Edge to replicate functions to AWS Regions.

  When you first add a Lambda@Edge trigger in CloudFront, a role named AWSServiceRoleForLambdaReplicator is created automatically to allow Lambda@Edge to replicate functions to AWS Regions. This role is required to use Lambda@Edge functions. The ARN for the AWSServiceRoleForLambdaReplicator role looks like the following example:

  `arn:aws:iam::123456789012:role/aws-service-role/replicator.lambda.amazonaws.com/AWSServiceRoleForLambdaReplicator`
+ **AWSServiceRoleForCloudFrontLogger** – CloudFront uses this role to push log files into CloudWatch. You can use log files to debug Lambda@Edge validation errors.

  The AWSServiceRoleForCloudFrontLogger role is created automatically when you add Lambda@Edge function association to allow CloudFront to push Lambda@Edge error log files to CloudWatch. The ARN for the AWSServiceRoleForCloudFrontLogger role looks like this:

  `arn:aws:iam::account_number:role/aws-service-role/logger.cloudfront.amazonaws.com/AWSServiceRoleForCloudFrontLogger`

A service-linked role makes setting up and using Lambda@Edge easier because you don’t have to manually add the necessary permissions. Lambda@Edge defines the permissions of its service-linked roles, and only Lambda@Edge can assume the roles. The defined permissions include the trust policy and the permissions policy. You can't attach the permissions policy to any other IAM entity.

You must remove any associated CloudFront or Lambda@Edge resources before you can delete a service-linked role. This helps protect your Lambda@Edge resources so that you don't remove a service-linked role that is still required to access active resources.

For more information about service-linked roles, see [Service-linked roles for CloudFront](security_iam_service-with-iam.md#security_iam_service-with-iam-roles-service-linked). 

### Service-linked role permissions for Lambda@Edge
<a name="slr-permissions-lambda-edge"></a>

Lambda@Edge uses two service-linked roles, named **AWSServiceRoleForLambdaReplicator** and **AWSServiceRoleForCloudFrontLogger**. The following sections describe the permissions for each of these roles.

**Contents**
+ [Service-linked role permissions for Lambda replicator](#slr-permissions-lambda-replicator)
+ [Service-linked role permissions for CloudFront logger](#slr-permissions-cloudfront-logger)

#### Service-linked role permissions for Lambda replicator
<a name="slr-permissions-lambda-replicator"></a>

This service-linked role allows Lambda to replicate Lambda@Edge functions to AWS Regions.

The AWSServiceRoleForLambdaReplicator service-linked role trusts the `replicator.lambda.amazonaws.com` service to assume the role.

The role permissions policy allows Lambda@Edge to complete the following actions on the specified resources:
+ `lambda:CreateFunction` on `arn:aws:lambda:*:*:function:*`
+ `lambda:DeleteFunction` on `arn:aws:lambda:*:*:function:*`
+ `lambda:DisableReplication` on `arn:aws:lambda:*:*:function:*`
+ `iam:PassRole` on `all AWS resources`
+  `cloudfront:ListDistributionsByLambdaFunction` on `all AWS resources`

#### Service-linked role permissions for CloudFront logger
<a name="slr-permissions-cloudfront-logger"></a>

This service-linked role allows CloudFront to push log files into CloudWatch so that you can debug Lambda@Edge validation errors.

The AWSServiceRoleForCloudFrontLogger service-linked role trusts the `logger.cloudfront.amazonaws.com` service to assume the role.

The role permissions policy allows Lambda@Edge to complete the following actions on the specified `arn:aws:logs:*:*:log-group:/aws/cloudfront/*` resource:
+ `logs:CreateLogGroup` ``
+ `logs:CreateLogStream`
+ `logs:PutLogEvents`

You must configure permissions to allow an IAM entity (such as a user, group, or role) to delete the Lambda@Edge service-linked roles. For more information, see [Service-linked role permissions](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html#service-linked-role-permissions) in the *IAM User Guide*.

### Creating service-linked roles for Lambda@Edge
<a name="create-slr-lambda-edge"></a>

You don’t typically manually create the service-linked roles for Lambda@Edge. The service creates the roles for you automatically in the following scenarios:
+ When you first create a trigger, the service creates the AWSServiceRoleForLambdaReplicator role (if it doesn’t already exist). This role allows Lambda to replicate Lambda@Edge functions to AWS Regions.

  If you delete the service-linked role, the role will be created again when you add a new trigger for Lambda@Edge in a distribution.
+ When you update or create a CloudFront distribution that has a Lambda@Edge association, the service creates the AWSServiceRoleForCloudFrontLogger role (if the role doesn’t already exist). This role allows CloudFront to push your log files to CloudWatch.

  If you delete the service-linked role, the role will be created again when you update or create a CloudFront distribution that has a Lambda@Edge association.

To manually create these service-linked roles, you can run the following AWS Command Line Interface (AWS CLI) commands:

**To create the AWSServiceRoleForLambdaReplicator role**
+ Run the following command.

  ```
  aws iam create-service-linked-role --aws-service-name replicator.lambda.amazonaws.com
  ```

**To create the AWSServiceRoleForCloudFrontLogger role**
+ Run the following command.

  ```
  aws iam create-service-linked-role --aws-service-name logger.cloudfront.amazonaws.com
  ```

### Editing Lambda@Edge service-linked roles
<a name="edit-slr-lambda-edge"></a>

Lambda@Edge doesn't allow you to edit the AWSServiceRoleForLambdaReplicator or AWSServiceRoleForCloudFrontLogger service-linked roles. After the service has created a service-linked role, you can't change the name of the role because various entities might reference the role. However, you can use IAM to edit the role description. For more information, see [Editing a service-linked role](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html#edit-service-linked-role) in the *IAM User Guide*.

### Supported AWS Regions for Lambda@Edge service-linked roles
<a name="slr-regions-lambda-edge"></a>

CloudFront supports using service-linked roles for Lambda@Edge in the following AWS Regions:
+ US East (N. Virginia) – `us-east-1`
+ US East (Ohio) – `us-east-2`
+ US West (N. California) – `us-west-1`
+ US West (Oregon) – `us-west-2`
+ Asia Pacific (Mumbai) – `ap-south-1`
+ Asia Pacific (Seoul) – `ap-northeast-2`
+ Asia Pacific (Singapore) – `ap-southeast-1`
+ Asia Pacific (Sydney) – `ap-southeast-2`
+ Asia Pacific (Tokyo) – `ap-northeast-1`
+ Europe (Frankfurt) – `eu-central-1`
+ Europe (Ireland) – `eu-west-1`
+ Europe (London) – `eu-west-2`
+ South America (São Paulo) – `sa-east-1`

# Write and create a Lambda@Edge function
<a name="lambda-edge-create-function"></a>

To use Lambda@Edge, you *write* the code for your AWS Lambda function. To help you write Lambda@Edge functions, see the following resources:
+  [Lambda@Edge event structure](lambda-event-structure.md) – Understand the event structure to use with Lambda@Edge.
+ [Lambda@Edge example functions](lambda-examples.md) – Example functions, such as A/B testing and generating an HTTP redirect.

The programming model for using Node.js or Python with Lambda@Edge is the same as using Lambda in an AWS Region. For more information, see [Building Lambda functions with Node.js](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html) or [Building Lambda functions with Python](https://docs.aws.amazon.com/lambda/latest/dg/lambda-python.html) in the *AWS Lambda Developer Guide*.

In your Lambda@Edge function, include the `callback` parameter and return the applicable object for request or response events:
+ **Request events** – Include the `cf.request` object in the response.

  If you're generating a response, include the `cf.response` object in the response. For more information, see [Generate HTTP responses in request triggers](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests). 
+ **Response events** – Include the `cf.response` object in the response.

After you write your own code or use one of the examples, you then create the function in Lambda. To create a function or edit an existing one, see the following topics:

**Topics**
+ [Create a Lambda@Edge function](lambda-edge-create-in-lambda-console.md)
+ [Edit a Lambda function](lambda-edge-edit-function.md)

 After you create the function in Lambda, you set up Lambda to run the function based on specific CloudFront events, which are called *triggers*. For more information, see [Add triggers for a Lambda@Edge function](lambda-edge-add-triggers.md).

# Create a Lambda@Edge function
<a name="lambda-edge-create-in-lambda-console"></a>

To set up AWS Lambda to run Lambda functions that are based on CloudFront events, follow this procedure.<a name="lambda-edge-create-function-procedure"></a>

**To create a Lambda@Edge function**

1. Sign in to the AWS Management Console and open the AWS Lambda console at [https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/).

1. If you already have one or more Lambda functions, choose **Create function**.

   If you've don't have any functions, choose **Get Started Now**.

1. In the Region list at the top of the page, choose **US East (N. Virginia)**.

1. Create a function using your own code or create a function starting with a CloudFront blueprint.
   + To create a function using your own code, choose **Author from scratch**. 
   + To display a list of blueprints for CloudFront, enter **cloudfront** in the filter field, and then choose **Enter**.

     If you find a blueprint that you want to use, choose the name of the blueprint.

1. In the **Basic information** section, specify the following values:

   1. **Name** – Enter a name for your function.

   1. **Role** – To get started quickly, choose **Create new role from template(s)**. You can also choose **Choose an existing role** or **Create a custom role**, and then follow the prompts to complete the information for this section.

   1. **Role name** – Enter a name for the role.

   1. **Policy templates** – Choose **Basic Edge Lambda permissions**.

1. If you chose **Author from scratch** in step 4, skip to step 7.

   If you chose a blueprint in step 4, the **cloudfront** section lets you create one trigger, which associates this function with a cache in a CloudFront distribution and a CloudFront event. We recommend that you choose **Remove** at this point, so there isn't a trigger for the function when it's created. Then you can add triggers later. 
**Tip**  
We recommend that you test and debug the function before adding triggers. If you add a trigger now, the function will run as soon as you create the function and it finishes replicating to AWS locations around the world, and the corresponding distribution is deployed.

1. Choose **Create function**.

   Lambda creates two versions of your function: \$1LATEST and Version 1. You can edit only the \$1LATEST version, but the console initially displays Version 1.

1. To edit the function, choose **Version 1** near the top of the page, under the ARN for the function. Then, on the **Versions** tab, choose **\$1LATEST**. (If you left the function and then returned to it, the button label is **Qualifiers**.)

1. On the **Configuration** tab, choose the applicable **Code entry type**. Then follow the prompts to edit or upload your code.

1. For **Runtime**, choose the value based on your function's code.

1. In the **Tags** section, add any applicable tags.

1. Choose **Actions**, and then choose **Publish new version**.

1. Enter a description for the new version of the function.

1. Choose **Publish**.

1. Test and debug the function. For more information about testing in the Lambda console, see [ Invoke a Lambda function using the console](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html#get-started-invoke-manually) in the *AWS Lambda Developer Guide*.

1. When you're ready to have the function execute for CloudFront events, publish another version and edit the function to add triggers. For more information, see [Add triggers for a Lambda@Edge function](lambda-edge-add-triggers.md).

# Edit a Lambda function
<a name="lambda-edge-edit-function"></a>

After you create a Lambda@Edge function, you can use the Lambda console to edit it.

**Notes**  
The original version is labeled \$1LATEST.
You can edit only the \$1LATEST version.
Each time you edit the \$1LATEST version, you must publish a new numbered version.
You can't create triggers for \$1LATEST.
When you publish a new version of a function, Lambda doesn't automatically copy triggers from the previous version to the new version. You must reproduce the triggers for the new version. 
When you add a trigger for a CloudFront event to a function, if there's already a trigger for the same distribution, cache behavior, and event for an earlier version of the same function, Lambda deletes the trigger from the earlier version.
After you make updates to a CloudFront distribution, like adding triggers, you must wait for the changes to propagate to edge locations before the functions you've specified in the triggers will work.<a name="lambda-edge-edit-function-procedure"></a>

**To edit a Lambda function**

1. Sign in to the AWS Management Console and open the AWS Lambda console at [https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/).

1. In the Region list at the top of the page, choose **US East (N. Virginia)**.

1. In the list of functions, choose the name of the function.

   By default, the console displays the \$1LATEST version. You can view earlier versions (choose **Qualifiers**), but you can only edit \$1LATEST.

1. On the **Code** tab, for **Code entry type**, choose to edit the code in the browser, upload a .zip file, or upload a file from Amazon S3.

1. Choose either **Save** or **Save and test**.

1. Choose **Actions**, and choose **Publish new version**. 

1. In the **Publish new version from \$1LATEST** dialog box, enter a description of the new version. This description appears in the list of versions, along with an automatically generated version number. 

1. Choose **Publish**.

   The new version automatically becomes the latest version. The version number appears on the **Version** in the upper-left corner of the page.
**Note**  
If you haven't added triggers for your function yet, see [Add triggers for a Lambda@Edge function](lambda-edge-add-triggers.md). 

1. Choose the **Triggers** tab.

1. Choose **Add trigger**.

1. In the **Add trigger** dialog box, choose the dotted box, and then choose **CloudFront**.
**Note**  
If you've already created one or more triggers for a function, CloudFront is the default service.

1. Specify the following values to indicate when you want the Lambda function to execute.

   1. **Distribution ID **– Choose the ID of the distribution that you want to add the trigger to.

   1. **Cache behavior** – Choose the cache behavior that specifies the objects that you want to execute the function on.

   1. **CloudFront event** – Choose the CloudFront event that causes the function to execute.

   1. **Enable trigger and replicate** – Select this check box so Lambda replicates the function to AWS Regions globally.

1. Choose **Submit**.

1. To add more triggers for this function, repeat steps 10 through 13.

For more information about testing and debugging the function in the Lambda console, see [ Invoke a Lambda function using the console](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html#get-started-invoke-manually) in the *AWS Lambda Developer Guide*.

When you're ready to have the function execute for CloudFront events, publish another version and edit the function to add triggers. For more information, see [Add triggers for a Lambda@Edge function](lambda-edge-add-triggers.md).

# Add triggers for a Lambda@Edge function
<a name="lambda-edge-add-triggers"></a>

A Lambda@Edge trigger is one combination of a CloudFront distribution, cache behavior, and event that causes a function to execute. For example, you can create a trigger that causes the function to execute when CloudFront receives a request from a viewer for a specific cache behavior you set up for your distribution. You can specify one or more CloudFront triggers. 

**Tip**  
When you create a CloudFront distribution, you specify settings that tell CloudFront how to respond when it receives different requests. The default settings are called the *default cache behavior* for the distribution. You can set up additional cache behaviors that define how CloudFront responds under specific circumstances, for example, when it receives a request for a specific file type. For more information, see [Cache behavior settings](DownloadDistValuesCacheBehavior.md).

When you first create a Lambda function, you can specify only *one* trigger. You can add more triggers to the same function later by using the Lambda console or by editing the distribution in the CloudFront console.
+ The Lambda console works well if you want to add more triggers to a function for the same CloudFront distribution.
+ The CloudFront console can be better if you want to add triggers for multiple distributions because it's easier to find the distribution that you want to update. You can also update other CloudFront settings at the same time.

**Topics**
+ [CloudFront events that can trigger a Lambda@Edge function](lambda-cloudfront-trigger-events.md)
+ [Choose the event to trigger the function](lambda-how-to-choose-event.md)
+ [Add triggers to a Lambda@Edge function](lambda-edge-add-triggers-console.md)

# CloudFront events that can trigger a Lambda@Edge function
<a name="lambda-cloudfront-trigger-events"></a>

For each cache behavior in a Amazon CloudFront distribution, you can add up to four triggers (associations) that cause a Lambda function to execute when specific CloudFront events occur. CloudFront triggers can be based on one of four CloudFront events, as shown in the following diagram.

![\[Conceptual graphic that shows how CloudFront trigger events for Lambda functions integrate with CloudFront.\]](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/images/cloudfront-events-that-trigger-lambda-functions.png)


The CloudFront events that can be used to trigger Lambda@Edge functions are the following:

**Viewer request**  
The function executes when CloudFront receives a request from a viewer, before it checks to see whether the requested object is in the CloudFront cache.  
The function doesn't execute in the following cases:  
+ When fetching a custom error page.
+ When CloudFront automatically redirects an HTTP request to HTTPS (when the value of the [Viewer protocol policy](DownloadDistValuesCacheBehavior.md#DownloadDistValuesViewerProtocolPolicy) is **Redirect HTTP to HTTPS**).

**Origin request**  
The function executes *only* when CloudFront forwards a request to your origin. When the requested object is in the CloudFront cache, the function doesn't execute.

**Origin response**  
The function executes after CloudFront receives a response from the origin and before it caches the object in the response. Note that the function executes even if an error is returned from the origin.  
The function doesn't execute in the following cases:  
+ When the requested file is in the CloudFront cache and is not expired.
+ When the response is generated from a function that was triggered by an origin request event.

**Viewer response**  
The function executes before returning the requested file to the viewer. Note that the function executes regardless of whether the file is already in the CloudFront cache.  
The function doesn't execute in the following cases:  
+ When the origin returns an HTTP status code of 400 or higher.
+ When a custom error page is returned.
+ When the response is generated from a function that was triggered by a viewer request event.
+ When CloudFront automatically redirects an HTTP request to HTTPS (when the value of the [Viewer protocol policy](DownloadDistValuesCacheBehavior.md#DownloadDistValuesViewerProtocolPolicy) is **Redirect HTTP to HTTPS**).

When you add multiple triggers to the same cache behavior, you can use them to run the same function or run different functions for each trigger. You can also associate the same function with more than one distribution.

**Note**  
When a CloudFront event triggers the execution of a Lambda function, the function must finish *before* CloudFront can continue.   
For example, if a Lambda function is triggered by a CloudFront viewer request event, CloudFront won't return a response to the viewer or forward the request to the origin until the Lambda function finishes running.   
This means that each request that triggers a Lambda function increases latency for the request, so you want the function to execute as fast as possible.

# Choose the event to trigger the function
<a name="lambda-how-to-choose-event"></a>

When you're deciding which CloudFront event you want to use to trigger a Lambda function, consider the following:

**I want CloudFront to cache objects that are changed by a Lambda function**  
To cache an object that was modified by a Lambda function so that CloudFront can serve the object from the edge location the next time it's requested, use the *origin request* or *origin response* event.   
This reduces the load on the origin, reduces latency for subsequent requests, and reduces the cost of invoking Lambda@Edge on subsequent requests.  
For example, if you want to add, remove, or change headers for objects that are returned by the origin and you want CloudFront to cache the result, use the origin response event.

**I want the function to execute for every request**  
To execute the function for every request that CloudFront receives for the distribution, use the *viewer request* or *viewer response* events.   
Origin request and origin response events occur only when a requested object isn't cached in an edge location and CloudFront forwards a request to the origin.

**I want the function to change the cache key**  
To change a value that you're using as a basis for caching, use the *viewer request* event.   
For example, if a function changes the URL to include a language abbreviation in the path (for example, because the user chose their language from a dropdown list), use the viewer request event:  
+ **URL in the viewer request** – https://example.com/en/index.html
+ **URL when the request comes from an IP address in Germany** – https://example.com/de/index.html
You also use the viewer request event if you're caching based on cookies or request headers.  
If the function changes cookies or headers, configure CloudFront to forward the applicable part of the request to the origin. For more information, see the following topics:  
+ [Cache content based on cookies](Cookies.md)
+ [Cache content based on request headers](header-caching.md)

**The function affects the response from the origin**  
To change the request in a way that affects the response from the origin, use the *origin request* event.   
Typically, most viewer request events aren't forwarded to the origin. CloudFront responds to a request with an object that's already in the edge cache. If the function changes the request based on an origin request event, CloudFront caches the response to the changed origin request.

# Add triggers to a Lambda@Edge function
<a name="lambda-edge-add-triggers-console"></a>

You can use the AWS Lambda console or Amazon CloudFront console to add a trigger to your Lambda@Edge function.

**Important**  
You can create triggers only for numbered versions of your function (not the **\$1LATEST**).

------
#### [ Lambda console ]<a name="lambda-edge-add-triggers-procedure"></a>

**To add triggers for CloudFront events to a Lambda@Edge function**

1. Sign in to the AWS Management Console and open the AWS Lambda console at [https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/).

1. In the Region list at the top of the page, choose **US East (N. Virginia)**.

1. On the **Functions** page, choose the name of the function that you want to add triggers for.

1. On the **Function overview** page, choose the **Versions** tab.

1. Choose the version that you want to add triggers to.

   After you choose a version, the name of the button changes to **Version: \$1LATEST** or **Version:** *version number*.

1. Choose the **Triggers** tab.

1. Choose **Add trigger**.

1. For **Trigger configuration**, choose **Select a source**, enter **cloudfront**, and then choose **CloudFront**.
**Note**  
If you've already created one or more triggers, CloudFront is the default service.

1. Specify the following values to indicate when you want the Lambda function to execute.

   1. **Distribution** – Choose the distribution that you want to add the trigger to.

   1. **Cache behavior** – Choose the cache behavior that specifies the objects that you want to execute the function on.
**Note**  
If you specify `*` for the cache behavior, the Lambda function deploys to the default cache behavior.

   1. **CloudFront event** – Choose the CloudFront event that causes the function to execute.

   1. **Include body** – Select this check box if you want to access the request body in your function. 

   1. **Confirm deploy to Lambda@Edge** – Select this check box so that AWS Lambda replicates the function to AWS Regions globally.

1. Choose **Add**.

   The function starts to process requests for the specified CloudFront events when the updated CloudFront distribution is deployed. To determine whether a distribution is deployed, choose **Distributions** in the navigation pane. When a distribution is deployed, the value of the **Status** column for the distribution changes from **Deploying** to the date and time of deployment.

------
#### [ CloudFront console ]<a name="lambda-create-functions-add-triggers-cloudfront-console-procedure"></a>

**To add triggers for CloudFront events to a Lambda@Edge function**

1. Get the ARN of the Lambda function that you want to add triggers for:

   1. Sign in to the AWS Management Console and open the AWS Lambda console at [https://console.aws.amazon.com/lambda/](https://console.aws.amazon.com/lambda/).

   1. In the list of Regions at the top of the page, choose **US East (N. Virginia)**.

   1. In the list of functions, choose name of the function that you want to add triggers to.

   1. On the **Function overview** page, choose the **Versions** tab, and choose the numbered version that you want to add triggers to.

   1. Choose the **Copy ARN** button to copy the ARN to your clipboard. The ARN for the Lambda function looks something like this:

      `arn:aws:lambda:us-east-1:123456789012:function:TestFunction:2`

      The number at the end (**2** in this example) is the version number of the function.

1. Open the CloudFront console at [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home).

1. In the list of distributions, choose the ID of the distribution that you want to add triggers to.

1. Choose the **Behaviors** tab.

1. Select the cache behavior that you want to add triggers to, and then choose **Edit**.

1. For **Function associations**, in the **Function type** list, choose **Lambda@Edge** for when you want the function to execute: for viewer requests, viewer responses, origin requests, or origin responses. 

   For more information, see [Choose the event to trigger the function](lambda-how-to-choose-event.md).

1. In the **Function ARN / Name** text box, paste the ARN of the Lambda function that you want to execute when the chosen event occurs. This is the value that you copied from the Lambda console.

1. Select **Include body** if you want to access the request body in your function.

   If you just want to replace the request body, you don't need to select this option.

1. To execute the same function for more event types, repeat steps 6 and 7.

1. Choose **Save changes**.

1. To add triggers to more cache behaviors for this distribution, repeat steps 5 through 10.

   The function starts to process requests for the specified CloudFront events when the updated CloudFront distribution is deployed. To determine whether a distribution is deployed, choose **Distributions** in the navigation pane. When a distribution is deployed, the value of the **Status** column for the distribution changes from **Deploying** to the time and date of deployment.

------

# Test and debug Lambda@Edge functions
<a name="lambda-edge-testing-debugging"></a>

It's important to test your Lambda@Edge function code standalone, to make sure that it completes the intended task, and to do integration testing, to make sure that the function works correctly with CloudFront. 

During integration testing or after your function has been deployed, you might need to debug CloudFront errors, such as HTTP 5xx errors. Errors can be an invalid response returned from the Lambda function, execution errors when the function is triggered, or errors due to execution throttling by the Lambda service. Sections in this topic share strategies for determining which type of failure is the issue, and then steps you can take to correct the problem.

**Note**  
When you review CloudWatch log files or metrics when you're troubleshooting errors, be aware that they are displayed or stored in the AWS Region closest to the location where the function executed. So, if you have a website or web application with users in the United Kingdom, and you have a Lambda function associated with your distribution, for example, you must change the Region to view the CloudWatch metrics or log files for the London AWS Region. For more information, see [Determine the Lambda@Edge Region](#lambda-edge-testing-debugging-determine-region).

**Topics**
+ [Test your Lambda@Edge functions](#lambda-edge-testing-debugging-test-function)
+ [Identify Lambda@Edge function errors in CloudFront](#lambda-edge-identifying-function-errors)
+ [Troubleshoot invalid Lambda@Edge function responses (validation errors)](#lambda-edge-testing-debugging-troubleshooting-invalid-responses)
+ [Troubleshoot Lambda@Edge function execution errors](#lambda-edge-testing-debugging-execution-errors)
+ [Determine the Lambda@Edge Region](#lambda-edge-testing-debugging-determine-region)
+ [Determine if your account pushes logs to CloudWatch](#lambda-edge-testing-debugging-cloudwatch-logs-enabled)

## Test your Lambda@Edge functions
<a name="lambda-edge-testing-debugging-test-function"></a>

There are two steps to testing your Lambda function: standalone testing and integration testing.

**Test standalone functionality**  
Before you add your Lambda function to CloudFront, make sure to test the functionality first by using the testing capabilities in the Lambda console or by using other methods. For more information about testing in the Lambda console, see [ Invoke a Lambda function using the console](https://docs.aws.amazon.com/lambda/latest/dg/getting-started.html#get-started-invoke-manually) in the *AWS Lambda Developer Guide*.

**Test your function's operation in CloudFront**  
It's important to complete integration testing, where your function is associated with a distribution and runs based on a CloudFront event. Make sure that the function is triggered for the right event, and returns a response that is valid and correct for CloudFront. For example, make sure that the event structure is correct, that only valid headers are included, and so on.  
As you iterate on integration testing with your function in the Lambda console, refer to the steps in the Lambda@Edge tutorial as you modify your code or change the CloudFront trigger that calls your function. For example, make sure that you're working in a numbered version of your function, as described in this step of the tutorial: [Step 4: Add a CloudFront trigger to run the function](lambda-edge-how-it-works-tutorial.md#lambda-edge-how-it-works-tutorial-add-trigger).   
As you make changes and deploy them, be aware that it will take several minutes for your updated function and CloudFront triggers to replicate across all Regions. This typically takes a few minutes but can take up to 15 minutes.  
You can check to see if replication is finished by going to the CloudFront console and viewing your distribution.  

**To check if your replication has finished deploying**

1. Open the CloudFront console at [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home).

1. Choose the distribution name.

1. Check for the distribution status to change from **In Progress** back to **Deployed**, which means that your function has been replicated. Then follow the steps in the next section to verify that the function works.
Be aware that testing in the console only validates your function's logic, and does not apply any service quotas (formerly known as limits) that are specific to Lambda@Edge.

## Identify Lambda@Edge function errors in CloudFront
<a name="lambda-edge-identifying-function-errors"></a>

After you've verified that your function logic works correctly, you might still see HTTP 5xx errors when your function runs in CloudFront. HTTP 5xx errors can be returned for a variety of reasons, which can include Lambda function errors or other issues in CloudFront.
+ If you use Lambda@Edge functions, you can use graphs in the CloudFront console to help track down what's causing the error, and then work to fix it. For example, you can see if HTTP 5xx errors are caused by CloudFront or by Lambda functions, and then, for specific functions, you can view related log files to investigate the issue.
+ To troubleshoot HTTP errors in general in CloudFront, see the troubleshooting steps in the following topic: [Troubleshooting error response status codes in CloudFront](troubleshooting-response-errors.md).

### What causes Lambda@Edge function errors in CloudFront
<a name="lambda-edge-testing-debugging-function-errors"></a>

There are several reasons why a Lambda function might cause an HTTP 5xx error, and the troubleshooting steps you should take depend on the type of error. Errors can be categorized as the following:

**A Lambda function execution error**  
An execution error results when CloudFront doesn't get a response from Lambda because there are unhandled exceptions in the function or there's an error in the code. For example, if the code includes callback(Error).

**An invalid Lambda function response is returned to CloudFront**  
After the function runs, CloudFront receives a response from Lambda. An error is returned if the object structure of the response doesn't conform to the [Lambda@Edge event structure](lambda-event-structure.md), or the response contains invalid headers or other invalid fields.

**The execution in CloudFront is throttled due to Lambda service quotas (formerly known as limits)**  
The Lambda service throttles executions in each Region, and returns an error if you exceed the quota. For more information, see [Quotas on Lambda@Edge](cloudfront-limits.md#limits-lambda-at-edge).

### How to determine the type of failure
<a name="lambda-edge-testing-debugging-failure-type"></a>

To help you decide where to focus as you debug and work to resolve errors returned by CloudFront, it's helpful to identify why CloudFront is returning an HTTP error. To get started, you can use the graphs provided in the **Monitoring** section of the CloudFront console on the AWS Management Console. For more information about viewing graphs in the **Monitoring** section of the CloudFront console, see [Monitor CloudFront metrics with Amazon CloudWatch](monitoring-using-cloudwatch.md).

The following graphs can be especially helpful when you want to track down whether errors are being returned by origins or by a Lambda function, and to narrow down the type of issue when it's an error from a Lambda function.

**Error rates graph**  
One of the graphs that you can view on the **Overview** tab for each of your distributions is an **Error rates** graph. This graph displays the rate of errors as a percentage of total requests coming to your distribution. The graph shows the total error rate, total 4xx errors, total 5xx errors, and total 5xx errors from Lambda functions. Based on the error type and volume, you can take steps to investigate and troubleshoot the cause.  

![\[Error rates graph for a CloudFront distribution\]](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/images/Distribution-error-rate-pct-full.png)

+ If you see Lambda errors, you can investigate further by looking at the specific types of errors that the function returns. The **Lambda@Edge errors** tab includes graphs that categorize function errors by type to help you pinpoint the issue for a specific function.
+ If you see CloudFront errors, you can troubleshoot and work to fix origin errors or change your CloudFront configuration. For more information, see [Troubleshooting error response status codes in CloudFront](troubleshooting-response-errors.md).

**Execution errors and invalid function responses graphs**  
The **Lambda@Edge errors** tab includes graphs that categorize the Lambda@Edge errors for a specific distribution, by type. For example, one graph shows all execution errors by AWS Region.  
To make it easier to troubleshoot issues, you can look for specific problems by opening and examining the log files for specific functions by Region.   

**To view log files for a specific function by Region**

1. On the **Lambda@Edge errors** tab, under **Associated Lambda@Edge functions**, choose the function name, and then choose **View metrics**. 

1. Next, on the page with your function name, in the upper-right corner, choose **View function logs**, and then choose a Region. 

   For example, if you see issues in the **Errors** graph for the US West (Oregon) Region, choose that Region from the dropdown list. This opens the Amazon CloudWatch console.

1. In the CloudWatch console for that Region, under **Log streams**, choose a log stream to view the events for the function.
In addition, read the following sections in this chapter for more recommendations about troubleshooting and fixing errors.

**Throttles graph**  
The **Lambda@Edge errors** tab also includes a **Throttles** graph. On occasion, the Lambda service throttles your function invocations on per Region basis, if you reach the regional concurrency quota (formerly known as limit). If you see a limit exceeded error, your function has reached a quota that the Lambda service imposes on executions in a Region. For more information, including how to request an increase in the quota, see [Quotas on Lambda@Edge](cloudfront-limits.md#limits-lambda-at-edge).  

![\[Throttle graph for Lambda@Edge function execution.\]](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/images/Lambda-throttles-page.png)


For an example about how to use this information in troubleshooting HTTP errors, see [Four steps for debugging your content delivery on AWS](https://aws.amazon.com/blogs/networking-and-content-delivery/four-steps-for-debugging-your-content-delivery-on-aws/).

## Troubleshoot invalid Lambda@Edge function responses (validation errors)
<a name="lambda-edge-testing-debugging-troubleshooting-invalid-responses"></a>

If you identify that your problem is a Lambda validation error, it means that your Lambda function is returning an invalid response to CloudFront. Follow the guidance in this section to take steps to review your function and make sure that your response conforms to CloudFront requirements. 

CloudFront validates the response from a Lambda function in two ways:
+ **The Lambda response must conform to the required object structure.** Examples of bad object structure include the following: unparsable JSON, missing required fields, and an invalid object in the response. For more information, see the [Lambda@Edge event structure](lambda-event-structure.md).
+ **The response must include only valid object values.** An error will occur if the response includes a valid object but has values that are not supported. Examples include the following: adding or updating disallowed or read-only headers (see [Restrictions on edge functions](edge-functions-restrictions.md)), exceeding the maximum body size (see *Restrictions on the Size of the Generated Response* in the Lambda@Edge [Errors](lambda-generating-http-responses.md#lambda-generating-http-responses-errors) topic), and invalid characters or values (see the [Lambda@Edge event structure](lambda-event-structure.md)).

When Lambda returns an invalid response to CloudFront, error messages are written to log files which CloudFront pushes to CloudWatch in the Region of where the Lambda function executed. It's the default behavior to send the log files to CloudWatch when there's an invalid response. However, if you associated a Lambda function with CloudFront before the functionality was released, it might not be enabled for your function. For more information, see *Determine if Your Account Pushes Logs to CloudWatch* later in the topic.

CloudFront pushes log files to the Region corresponding to where your function executed, in the log group that's associated with your distribution. Log groups have the following format: `/aws/cloudfront/LambdaEdge/DistributionId`, where *DistributionId* is your distribution's ID. To determine the Region where you can find the CloudWatch log files, see *Determining the Lambda@Edge Region* later in this topic.

If the error is reproducible, you can create a new request that results in the error and then find the request id in a failed CloudFront response (`X-Amz-Cf-Id` header) to locate a single failure in log files. The log file entry includes information that can help you identify why the error is being returned, and also lists the corresponding Lambda request id so you can analyze the root cause in the context of a single request.

If an error is intermittent, you can use CloudFront access logs to find the request id for a request that has failed, and then search CloudWatch logs for the corresponding error messages. For more information, see the previous section, *Determining the Type of Failure*.

## Troubleshoot Lambda@Edge function execution errors
<a name="lambda-edge-testing-debugging-execution-errors"></a>

If the problem is a Lambda execution error, it can be helpful to create logging statements for Lambda functions, to write messages to CloudWatch log files that monitor the execution of your function in CloudFront and determine if it's working as expected. Then you can search for those statements in the CloudWatch log files to verify that your function is working.

**Note**  
Even if you haven't changed your Lambda@Edge function, updates to the Lambda function execution environment might affect it and could return an execution error. For information about testing and migrating to a later version, see [Upcoming updates to the AWS Lambda and AWS Lambda@Edge execution environment.](https://aws.amazon.com/blogs/compute/upcoming-updates-to-the-aws-lambda-execution-environment/)

## Determine the Lambda@Edge Region
<a name="lambda-edge-testing-debugging-determine-region"></a>

To see the Regions where your Lambda@Edge function is receiving traffic, view metrics for the function on the CloudFront console on the AWS Management Console. Metrics are displayed for each AWS Region. On the same page, you can choose a Region and view log files for that Region so you can investigate issues. You must review CloudWatch log files in the correct AWS Region to see the log files created when CloudFront executed your Lambda function.

For more information about viewing graphs in the **Monitoring** section of the CloudFront console, see [Monitor CloudFront metrics with Amazon CloudWatch](monitoring-using-cloudwatch.md).

## Determine if your account pushes logs to CloudWatch
<a name="lambda-edge-testing-debugging-cloudwatch-logs-enabled"></a>

By default, CloudFront enables logging invalid Lambda function responses, and pushes the log files to CloudWatch by using one of the [Service-linked roles for Lambda@Edge](lambda-edge-permissions.md#using-service-linked-roles-lambda-edge). If you have Lambda@Edge functions that you added to CloudFront before the invalid Lambda function response log feature was released, logging is enabled when you next update your Lambda@Edge configuration, for example, by adding a CloudFront trigger.

You can verify that pushing the log files to CloudWatch is enabled for your account by doing the following:
+ **Check to see if the logs appear in CloudWatch** – Make sure that you look in the Region where the Lambda@Edge function executed. For more information, see [Determine the Lambda@Edge Region](#lambda-edge-testing-debugging-determine-region).
+ **Determine if the related service-linked role exists in your account in IAM –** You must have the IAM role `AWSServiceRoleForCloudFrontLogger` in your account. For more information about this role, see [Service-linked roles for Lambda@Edge](lambda-edge-permissions.md#using-service-linked-roles-lambda-edge).

# Delete Lambda@Edge functions and replicas
<a name="lambda-edge-delete-replicas"></a>

You can delete a Lambda@Edge function only when the replicas of the function have been deleted by CloudFront. Replicas of a Lambda function are automatically deleted in the following situations:
+ After you remove the last association for the function from all of your CloudFront distributions. If more than one distribution uses a function, the replicas are deleted only after you remove the function association from the last distribution.
+ After you delete the last distribution that a function was associated with.

Replicas are typically deleted within a few hours. You cannot manually delete Lambda@Edge function replicas. This helps prevent a situation where a replica is deleted that is still in use, which would result in an error.

**Warning**  
Don't build applications that use Lambda@Edge function replicas outside of CloudFront. These replicas are deleted when their associations with distributions are removed, or when distributions themselves are deleted. The replica that an outside application depends on might be removed without warning, causing it to fail.

**To delete a Lambda@Edge function association from a CloudFront distribution**

1. Sign in to the AWS Management Console and open the CloudFront console at [https://console.aws.amazon.com/cloudfront/v4/home](https://console.aws.amazon.com/cloudfront/v4/home).

1. Choose the ID of the distribution with the Lambda@Edge function association that you want to delete.

1. Choose the **Behaviors** tab.

1. Select the cache behavior that has the Lambda@Edge function association that you want to delete, and then choose **Edit**.

1. Under **Function associations**, **Function type**, choose **No association** to delete the Lambda@Edge function association.

1. Choose **Save changes**.

After you delete a Lambda@Edge function association from a CloudFront distribution, you can optionally delete the Lambda function or function version from AWS Lambda. Wait a few hours after deleting the function association so that the Lambda@Edge function replicas can be cleaned up. After that, you can delete the function by using the Lambda console, AWS CLI, Lambda API, or an AWS SDK.

You can also delete a specific *version* of a Lambda function if the version doesn't have any CloudFront distributions associated with it. After removing all the associations for a Lambda function version, wait a few hours. Then you can delete the function version.

# Lambda@Edge event structure
<a name="lambda-event-structure"></a>

The following topics describe the request and response event objects that CloudFront passes to a Lambda@Edge function when it's triggered.

**Topics**
+ [Dynamic origin selection](#lambda-event-content-based-routing)
+ [Request events](#lambda-event-structure-request)
+ [Response events](#lambda-event-structure-response)

## Dynamic origin selection
<a name="lambda-event-content-based-routing"></a>

You can use [the path pattern in a cache behavior](DownloadDistValuesCacheBehavior.md#DownloadDistValuesPathPattern) to route requests to an origin based on the path and name of the requested object, such as `images/*.jpg`. Using Lambda@Edge, you can also route requests to an origin based on other characteristics, such as the values in request headers. 

There are a number of ways that this dynamic origin selection can be useful. For example, you can distribute requests across origins in different geographic areas to help with global load balancing. Or you can selectively route requests to different origins that each serve a particular function: bot handling, SEO optimization, authentication, and so on. For code examples that demonstrate how to use this feature, see [Content-based dynamic origin selection - examples](lambda-examples.md#lambda-examples-content-based-routing-examples).

In the CloudFront origin request event, the `origin` object in the event structure contains information about the origin that the request would be routed to, based on the path pattern. You can update the values in the `origin` object to route a request to a different origin. When you update the `origin` object, you don't need to define the origin in the distribution. You can also replace an Amazon S3 origin object with a custom origin object, and vice versa. You can only specify a single origin per request, though; either a custom origin or an Amazon S3 origin, but not both.

## Request events
<a name="lambda-event-structure-request"></a>

The following topics show the structure of the object that CloudFront passes to a Lambda function for [viewer and origin request events](lambda-cloudfront-trigger-events.md). These examples show a `GET` request with no body. Following the examples is a list of all the possible fields in viewer and origin request events.

**Topics**
+ [Example viewer request](#example-viewer-request)
+ [Example origin request](#example-origin-request)
+ [Request event fields](#request-event-fields)

### Example viewer request
<a name="example-viewer-request"></a>

The following example shows a viewer request event object.

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "viewer-request",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "host": [
              {
                "key": "Host",
                "value": "d111111abcdef8.cloudfront.net"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "curl/7.66.0"
              }
            ],
            "accept": [
              {
                "key": "accept",
                "value": "*/*"
              }
            ]
          },
          "method": "GET",
          "querystring": "",
          "uri": "/"
        }
      }
    }
  ]
}
```

### Example origin request
<a name="example-origin-request"></a>

The following example shows an origin request event object.

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "origin-request",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "x-forwarded-for": [
              {
                "key": "X-Forwarded-For",
                "value": "203.0.113.178"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "Amazon CloudFront"
              }
            ],
            "via": [
              {
                "key": "Via",
                "value": "2.0 2afae0d44e2540f472c0635ab62c232b.cloudfront.net (CloudFront)"
              }
            ],
            "host": [
              {
                "key": "Host",
                "value": "example.org"
              }
            ],
            "cache-control": [
              {
                "key": "Cache-Control",
                "value": "no-cache"
              }
            ]
          },
          "method": "GET",
          "origin": {
            "custom": {
              "customHeaders": {},
              "domainName": "example.org",
              "keepaliveTimeout": 5,
              "path": "",
              "port": 443,
              "protocol": "https",
              "readTimeout": 30,
              "responseCompletionTimeout": 30,
              "sslProtocols": [
                "TLSv1",
                "TLSv1.1",
                "TLSv1.2"
              ]
            }
          },
          "querystring": "",
          "uri": "/"
        }
      }
    }
  ]
}
```

### Request event fields
<a name="request-event-fields"></a>

Request event object data is contained in two subobjects: `config` (`Records.cf.config`) and `request` (`Records.cf.request`). The following lists describe each subobject's fields.

#### Fields in the config object
<a name="request-event-fields-config"></a>

The following list describes the fields in the `config` object (`Records.cf.config`).

**`distributionDomainName` (read-only)**  
The domain name of the distribution that's associated with the request.

**`distributionID` (read-only)**  
The ID of the distribution that's associated with the request.

**`eventType` (read-only)**  
The type of trigger that's associated with the request: `viewer-request` or `origin-request`.

**`requestId` (read-only)**  
An encrypted string that uniquely identifies a viewer-to-CloudFront request. The `requestId` value also appears in CloudFront access logs as `x-edge-request-id`. For more information, see [Access logs (standard logs)](AccessLogs.md) and [Log file fields](standard-logs-reference.md#BasicDistributionFileFormat).

#### Fields in the request object
<a name="request-event-fields-request"></a>

The following list describes the fields in the `request` object (`Records.cf.request`).

**`clientIp` (read-only)**  
The IP address of the viewer that made the request. If the viewer used an HTTP proxy or a load balancer to send the request, the value is the IP address of the proxy or load balancer.

**headers (read/write)**  
The headers in the request. Note the following:  
+ The keys in the `headers` object are lowercase versions of standard HTTP header names. Using lowercase keys gives you case-insensitive access to the header values.
+ Each header object (for example, `headers["accept"]` or `headers["host"]`) is an array of key–value pairs. For a given header, the array contains one key–value pair for each value in the request.
+ `key` contains the case-sensitive name of the header as it appeared in the HTTP request; for example, `Host`, `User-Agent`, `X-Forwarded-For`, `Cookie`, and so on.
+ `value` contains the header value as it appeared in the HTTP request.
+ When your Lambda function adds or modifies request headers and you don't include the header `key` field, Lambda@Edge automatically inserts a header `key` using the header name that you provide. Regardless of how you've formatted the header name, the header key that's inserted automatically is formatted with initial capitalization for each part, separated by hyphens (-).

  For example, you can add a header like the following, without a header `key`:

  ```
  "user-agent": [
    {
      "value": "ExampleCustomUserAgent/1.X.0"
    }
  ]
  ```

  In this example, Lambda@Edge automatically inserts `"key": "User-Agent"`.
For information about restrictions on header usage, see [Restrictions on edge functions](edge-functions-restrictions.md).

**`method` (read-only)**  
The HTTP method of the request.

**`querystring` (read/write)**  
The query string, if any, in the request. If the request doesn't include a query string, the event object still includes `querystring` with an empty value. For more information about query strings, see [Cache content based on query string parameters](QueryStringParameters.md).

**`uri` (read/write)**  
The relative path of the requested object. If your Lambda function modifies the `uri` value, note the following:  
+ The new `uri` value must begin with a forward slash (/).
+ When a function changes the `uri` value, that changes the object that the viewer is requesting.
+ When a function changes the `uri` value, that *doesn't* change the cache behavior for the request or the origin that the request is sent to.

**`body` (read/write)**  
The body of the HTTP request. The `body` structure can contain the following fields:    
**`inputTruncated` (read-only)**  
A Boolean flag that indicates whether the body was truncated by Lambda@Edge. For more information, see [Restrictions on the request body with the include body option](lambda-at-edge-function-restrictions.md#lambda-at-edge-restrictions-request-body).  
**`action` (read/write)**  
The action that you intend to take with the body. The options for `action` are the following:  
+ `read-only:` This is the default. When returning the response from the Lambda function, if `action` is read-only, Lambda@Edge ignores any changes to `encoding` or `data`.
+ `replace:` Specify this when you want to replace the body sent to the origin.  
**`encoding` (read/write)**  
The encoding for the body. When Lambda@Edge exposes the body to the Lambda function, it first converts the body to base64-encoding. If you choose `replace` for the `action` to replace the body, you can opt to use `base64` (the default) or `text` encoding. If you specify `encoding` as `base64` but the body is not valid base64, CloudFront returns an error.  
**`data` (read/write)**  
The request body content. 

**`origin` (read/write) (origin events only)**  
The origin to send the request to. The `origin` structure must contain *exactly one origin*, which can be a custom origin or an Amazon S3 origin.  
Depending on your origin type that you specify (custom or Amazon S3 origins), you must specify the following fields in your request:    
**`customHeaders` (read/write) (custom and Amazon S3 origins)**  
(Optional) You can include custom headers with the request by specifying a header name and value pair for each custom header. You can't add headers that are disallowed, and a header with the same name can't be present in `Records.cf.request.headers`. The [notes about request headers](#request-event-fields-request-headers) also apply to custom headers. For more information, see [Custom headers that CloudFront can’t add to origin requests](add-origin-custom-headers.md#add-origin-custom-headers-denylist) and [Restrictions on edge functions](edge-functions-restrictions.md).  
**`domainName` (read/write) (custom and Amazon S3 origins)**  
The domain name of the origin. The domain name can't be empty.  
+ **For custom origins** – Specify a DNS domain name, such as `www.example.com`. The domain name can't include a colon (:), and can't be an IP address. The domain name can be up to 253 characters.
+ **For Amazon S3 origins** – Specify the DNS domain name of the Amazon S3 bucket, such as `amzn-s3-demo-bucket.s3.eu-west-1.amazonaws.com`. The name can be up to 128 characters, and must be all lowercase.  
**`path` (read/write) (custom and Amazon S3 origins)**  
The directory path at the origin where the request should locate content. The path should start with a forward slash (/) but shouldn't end with one (for example, it shouldn't end with `example-path/`). For custom origins only, the path should be URL encoded and have a maximum length of 255 characters.  
**`keepaliveTimeout` (read/write) (custom origins only)**  
How long, in seconds, that CloudFront should try to maintain the connection to the origin after receiving the last packet of the response. The value must be a number from 1–120, inclusive.  
**`port` (read/write) (custom origins only)**  
The port that CloudFront should connect to at your custom origin. The port must be 80, 443, or a number in the range of 1024–65535, inclusive.  
**`protocol` (read/write) (custom origins only)**  
The connection protocol that CloudFront should use when connecting to your origin. The value can be `http` or `https`.  
**`readTimeout` (read/write) (custom and Amazon S3 origins)**  
How long, in seconds, CloudFront should wait for a response after sending a request to your origin. This also specifies how long CloudFront should wait after receiving a packet of a response before receiving the next packet. The value must be a number from 1–120, inclusive.  
If you need a higher quota, see [Response timeout per origin](cloudfront-limits.md#limits-web-distributions).  
**`responseCompletionTimeout` (read/write) (custom and Amazon S3 origins)**  
The time (in seconds) that a request from CloudFront to the origin can stay open and wait for a response. If the complete response isn't received from the origin by this time, CloudFront ends the connection.  
The value for `responseCompletionTimeout` must be equal to or greater than the value for the `readTimeout`. If you set this value to 0, it removes any previous value you set and returns to the default. You can also accomplish this by deleting the `responseCompletionTimeout` field from the event request.   
**`sslProtocols` (read/write) (custom origins only)**  
The minimum SSL/TLS protocol that CloudFront can use when establishing an HTTPS connection with your origin. Values can be any of the following: `TLSv1.2`, `TLSv1.1`, `TLSv1`, or `SSLv3`.  
**`authMethod` (read/write) (Amazon S3 origins only)**  
If you're using an [origin access identity (OAI)](private-content-restricting-access-to-s3.md#private-content-restricting-access-to-s3-oai), set this field to `origin-access-identity`. If you aren't using an OAI, set it to `none`. If you set `authMethod` to `origin-access-identity`, there are several requirements:   
+ You must specify the `region` (see the following field).
+ You must use the same OAI when you change the request from one Amazon S3 origin to another.
+ You can't use an OAI when you change the request from a custom origin to an Amazon S3 origin.
This field does not support [origin access control (OAC)](private-content-restricting-access-to-s3.md).  
**`region` (read/write) (Amazon S3 origins only)**  
The AWS Region of your Amazon S3 bucket. This is required only when you set `authMethod` to `origin-access-identity`.

## Response events
<a name="lambda-event-structure-response"></a>

The following topics show the structure of the object that CloudFront passes to a Lambda function for [viewer and origin response events](lambda-cloudfront-trigger-events.md). Following the examples is a list of all the possible fields in viewer and origin response events.

**Topics**
+ [Example origin response](#lambda-event-structure-response-origin)
+ [Example viewer response](#lambda-event-structure-response-viewer)
+ [Response event fields](#response-event-fields)

### Example origin response
<a name="lambda-event-structure-response-origin"></a>

The following example shows an origin response event object.

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "origin-response",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "x-forwarded-for": [
              {
                "key": "X-Forwarded-For",
                "value": "203.0.113.178"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "Amazon CloudFront"
              }
            ],
            "via": [
              {
                "key": "Via",
                "value": "2.0 8f22423015641505b8c857a37450d6c0.cloudfront.net (CloudFront)"
              }
            ],
            "host": [
              {
                "key": "Host",
                "value": "example.org"
              }
            ],
            "cache-control": [
              {
                "key": "Cache-Control",
                "value": "no-cache"
              }
            ]
          },
          "method": "GET",
          "origin": {
            "custom": {
              "customHeaders": {},
              "domainName": "example.org",
              "keepaliveTimeout": 5,
              "path": "",
              "port": 443,
              "protocol": "https",
              "readTimeout": 30,
              "responseCompletionTimeout": 30,
              "sslProtocols": [
                "TLSv1",
                "TLSv1.1",
                "TLSv1.2"
              ]
            }
          },
          "querystring": "",
          "uri": "/"
        },
        "response": {
          "headers": {
            "access-control-allow-credentials": [
              {
                "key": "Access-Control-Allow-Credentials",
                "value": "true"
              }
            ],
            "access-control-allow-origin": [
              {
                "key": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ],
            "date": [
              {
                "key": "Date",
                "value": "Mon, 13 Jan 2020 20:12:38 GMT"
              }
            ],
            "referrer-policy": [
              {
                "key": "Referrer-Policy",
                "value": "no-referrer-when-downgrade"
              }
            ],
            "server": [
              {
                "key": "Server",
                "value": "ExampleCustomOriginServer"
              }
            ],
            "x-content-type-options": [
              {
                "key": "X-Content-Type-Options",
                "value": "nosniff"
              }
            ],
            "x-frame-options": [
              {
                "key": "X-Frame-Options",
                "value": "DENY"
              }
            ],
            "x-xss-protection": [
              {
                "key": "X-XSS-Protection",
                "value": "1; mode=block"
              }
            ],
            "content-type": [
              {
                "key": "Content-Type",
                "value": "text/html; charset=utf-8"
              }
            ],
            "content-length": [
              {
                "key": "Content-Length",
                "value": "9593"
              }
            ]
          },
          "status": "200",
          "statusDescription": "OK"
        }
      }
    }
  ]
}
```

### Example viewer response
<a name="lambda-event-structure-response-viewer"></a>

The following example shows a viewer response event object.

```
{
  "Records": [
    {
      "cf": {
        "config": {
          "distributionDomainName": "d111111abcdef8.cloudfront.net",
          "distributionId": "EDFDVBD6EXAMPLE",
          "eventType": "viewer-response",
          "requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
        },
        "request": {
          "clientIp": "203.0.113.178",
          "headers": {
            "host": [
              {
                "key": "Host",
                "value": "d111111abcdef8.cloudfront.net"
              }
            ],
            "user-agent": [
              {
                "key": "User-Agent",
                "value": "curl/7.66.0"
              }
            ],
            "accept": [
              {
                "key": "accept",
                "value": "*/*"
              }
            ]
          },
          "method": "GET",
          "querystring": "",
          "uri": "/"
        },
        "response": {
          "headers": {
            "access-control-allow-credentials": [
              {
                "key": "Access-Control-Allow-Credentials",
                "value": "true"
              }
            ],
            "access-control-allow-origin": [
              {
                "key": "Access-Control-Allow-Origin",
                "value": "*"
              }
            ],
            "date": [
              {
                "key": "Date",
                "value": "Mon, 13 Jan 2020 20:14:56 GMT"
              }
            ],
            "referrer-policy": [
              {
                "key": "Referrer-Policy",
                "value": "no-referrer-when-downgrade"
              }
            ],
            "server": [
              {
                "key": "Server",
                "value": "ExampleCustomOriginServer"
              }
            ],
            "x-content-type-options": [
              {
                "key": "X-Content-Type-Options",
                "value": "nosniff"
              }
            ],
            "x-frame-options": [
              {
                "key": "X-Frame-Options",
                "value": "DENY"
              }
            ],
            "x-xss-protection": [
              {
                "key": "X-XSS-Protection",
                "value": "1; mode=block"
              }
            ],
            "age": [
              {
                "key": "Age",
                "value": "2402"
              }
            ],
            "content-type": [
              {
                "key": "Content-Type",
                "value": "text/html; charset=utf-8"
              }
            ],
            "content-length": [
              {
                "key": "Content-Length",
                "value": "9593"
              }
            ]
          },
          "status": "200",
          "statusDescription": "OK"
        }
      }
    }
  ]
}
```

### Response event fields
<a name="response-event-fields"></a>

Response event object data is contained in three subobjects: `config` (`Records.cf.config`), `request` (`Records.cf.request`), and `response` (`Records.cf.response`). For more information about the fields in the request object, see [Fields in the request object](#request-event-fields-request). The following lists describe the fields in the `config` and `response` subobjects.

#### Fields in the config object
<a name="response-event-fields-config"></a>

The following list describes the fields in the `config` object (`Records.cf.config`).

**`distributionDomainName` (read-only)**  
The domain name of the distribution that's associated with the response.

**`distributionID` (read-only)**  
The ID of the distribution that's associated with the response.

**`eventType` (read-only)**  
The type of trigger that's associated with the response: `origin-response` or `viewer-response`.

**`requestId` (read-only)**  
An encrypted string that uniquely identifies the viewer-to-CloudFront request that this response is associated with. The `requestId` value also appears in CloudFront access logs as `x-edge-request-id`. For more information, see [Access logs (standard logs)](AccessLogs.md) and [Log file fields](standard-logs-reference.md#BasicDistributionFileFormat).

#### Fields in the response object
<a name="response-event-fields-response"></a>

The following list describes the fields in the `response` object (`Records.cf.response`). For information about using a Lambda@Edge function to generate an HTTP response, see [Generate HTTP responses in request triggers](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests).

**`headers` (read/write)**  
The headers in the response. Note the following:  
+ The keys in the `headers` object are lowercase versions of standard HTTP header names. Using lowercase keys gives you case-insensitive access to the header values.
+ Each header object (for example, `headers["content-type"]` or `headers["content-length"]`) is an array of key–value pairs. For a given header, the array contains one key–value pair for each value in the response.
+ `key` contains the case-sensitive name of the header as it appears in the HTTP response; for example, `Content-Type`, `Content-Length`, `Cookie`, and so on.
+ `value` contains the header value as it appears in the HTTP response.
+ When your Lambda function adds or modifies response headers and you don't include the header `key` field, Lambda@Edge automatically inserts a header `key` using the header name that you provide. Regardless of how you've formatted the header name, the header key that's inserted automatically is formatted with initial capitalization for each part, separated by hyphens (-).

  For example, you can add a header like the following, without a header `key`:

  ```
  "content-type": [
    {
      "value": "text/html;charset=UTF-8"
    }
  ]
  ```

  In this example, Lambda@Edge automatically inserts `"key": "Content-Type"`.
For information about restrictions on header usage, see [Restrictions on edge functions](edge-functions-restrictions.md).

**`status`**  
The HTTP status code of the response.

**`statusDescription`**  
The HTTP status description of the response.

# Work with requests and responses
<a name="lambda-generating-http-responses"></a>

To use Lambda@Edge requests and responses, see the following topics:

**Topics**
+ [Use Lambda@Edge functions with origin failover](#lambda-and-origin-failover)
+ [Generate HTTP responses in request triggers](#lambda-generating-http-responses-in-requests)
+ [Update HTTP responses in origin response triggers](#lambda-updating-http-responses)
+ [Access the request body by choosing the include body option](#lambda-include-body-access)

## Use Lambda@Edge functions with origin failover
<a name="lambda-and-origin-failover"></a>

You can use Lambda@Edge functions with CloudFront distributions that you've set up with origin groups, for example, for origin failover that you configure to help ensure high availability. To use a Lambda function with an origin group, specify the function in an origin request or origin response trigger for an origin group when you create the cache behavior.

For more information, see the following:
+ **Create origin groups:** [Create an origin group](high_availability_origin_failover.md#concept_origin_groups.creating)
+ **How origin failover works with Lambda@Edge:** [Use origin failover with Lambda@Edge functions](high_availability_origin_failover.md#concept_origin_groups.lambda)

## Generate HTTP responses in request triggers
<a name="lambda-generating-http-responses-in-requests"></a>

When CloudFront receives a request, you can use a Lambda function to generate an HTTP response that CloudFront returns directly to the viewer without forwarding the response to the origin. Generating HTTP responses reduces the load on the origin, and typically also reduces latency for the viewer.

Some common scenarios for generating HTTP responses include the following:
+ Returning a small webpage to the viewer
+ Returning an HTTP 301 or 302 status code to redirect the user to another webpage
+ Returning an HTTP 401 status code to the viewer when the user hasn't authenticated

A Lambda@Edge function can generate an HTTP response when the following CloudFront events occur:

**Viewer request events**  
When a function is triggered by a viewer request event, CloudFront returns the response to the viewer and doesn't cache it.

**Origin request events**  
When a function is triggered by an origin request event, CloudFront checks the edge cache for a response that was previously generated by the function.   
+ If the response is in the cache, the function isn't executed and CloudFront returns the cached response to the viewer.
+ If the response isn't in the cache, the function is executed, CloudFront returns the response to the viewer, and also caches it.

To see some sample code for generating HTTP responses, see [Lambda@Edge example functions](lambda-examples.md). You can also replace the HTTP responses in response triggers. For more information, see [Update HTTP responses in origin response triggers](#lambda-updating-http-responses).

### Programming model
<a name="lambda-generating-http-responses-programming-model"></a>

This section describes the programming model for using Lambda@Edge to generate HTTP responses.

**Topics**
+ [Response object](#lambda-generating-http-responses-object)
+ [Errors](#lambda-generating-http-responses-errors)
+ [Required fields](#lambda-generating-http-responses-required-fields)

#### Response object
<a name="lambda-generating-http-responses-object"></a>

The response you return as the `result` parameter of the `callback` method should have the following structure (note that only the `status` field is required).

```
const response = {
    body: 'content',
    bodyEncoding: 'text' | 'base64',
    headers: {
        'header name in lowercase': [{
            key: 'header name in standard case',
            value: 'header value'
         }],
         ...
    },
    status: 'HTTP status code (string)',
    statusDescription: 'status description'
};
```

The response object can include the following values:

**`body`**  
The body, if any, that you want CloudFront to return in the generated response.

**`bodyEncoding`**  
The encoding for the value that you specified in the `body`. The only valid encodings are `text` and `base64`. If you include `body` in the `response` object but omit `bodyEncoding`, CloudFront treats the body as text.  
If you specify `bodyEncoding` as `base64` but the body is not valid base64, CloudFront returns an error.

**`headers`**  
Headers that you want CloudFront to return in the generated response. Note the following:  
+ The keys in the `headers` object are lowercase versions of standard HTTP header names. Using lowercase keys gives you case-insensitive access to the header values.
+ Each header (for example, `headers["accept"]` or `headers["host"]`) is an array of key-value pairs. For a given header, the array contains one key-value pair for each value in the generated response.
+ `key` (optional) is the case-sensitive name of the header as it appears in an HTTP request; for example, `accept` or `host`.
+ Specify `value` as a header value.
+ If you do not include the header key portion of the key-value pair, Lambda@Edge automatically inserts a header key using the header name that you provide. Regardless of how you've formatted the header name, the header key that is inserted is automatically formatted with initial capitalization for each part, separated by hyphens (-).

  For example, you can add a header like the following, without a header key: `'content-type': [{ value: 'text/html;charset=UTF-8' }]`

  In this example, Lambda@Edge creates the following header key: `Content-Type`.
For information about restrictions on header usage, see [Restrictions on edge functions](edge-functions-restrictions.md).

**`status`**  
The HTTP status code. Provide the status code as a string. CloudFront uses the provided status code for the following:  
+ Return in the response
+ Cache in the CloudFront edge cache, when the response was generated by a function that was triggered by an origin request event
+ Log in CloudFront [Access logs (standard logs)](AccessLogs.md)
If the `status` value isn't between 200 and 599, CloudFront returns an error to the viewer.

**`statusDescription`**  
The description that you want CloudFront to return in the response, to accompany the HTTP status code. You don't need to use standard descriptions, such as `OK` for an HTTP status code of 200.

#### Errors
<a name="lambda-generating-http-responses-errors"></a>

The following are possible errors for generated HTTP responses.

**Response Contains a Body and Specifies 204 (No Content) for Status**  
When a function is triggered by a viewer request, CloudFront returns an HTTP 502 status code (Bad Gateway) to the viewer when both of the following are true:  
+ The value of `status` is 204 (No Content)
+ The response includes a value for `body`
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 message body.

**Restrictions on the Size of the Generated Response**  
The maximum size of a response that is generated by a Lambda function depends on the event that triggered the function:  
+ **Viewer request events** – 40 KB
+ **Origin request events** – 1 MB
If the response is larger than the allowed size, CloudFront returns an HTTP 502 status code (Bad Gateway) to the viewer.

#### Required fields
<a name="lambda-generating-http-responses-required-fields"></a>

The `status` field is required. 

All other fields are optional.

## Update HTTP responses in origin response triggers
<a name="lambda-updating-http-responses"></a>

When CloudFront receives an HTTP response from the origin server, if there is an origin-response trigger associated with the cache behavior, you can modify the HTTP response to override what was returned from the origin.

Some common scenarios for updating HTTP responses include the following:
+ Changing the status to set an HTTP 200 status code and creating static body content to return to the viewer when an origin returns an error status code (4xx or 5xx). For sample code, see [Example: Use an origin response trigger to update the error status code to 200](lambda-examples.md#lambda-examples-custom-error-static-body).
+ Changing the status to set an HTTP 301 or HTTP 302 status code, to redirect the user to another website when an origin returns an error status code (4xx or 5xx). For sample code, see [Example: Use an origin response trigger to update the error status code to 302](lambda-examples.md#lambda-examples-custom-error-new-site).

**Note**  
The function must return a status value between `200` and `599` (inclusive), otherwise CloudFront returns an error to the viewer.

You can also replace the HTTP responses in viewer and origin request events. For more information, see [Generate HTTP responses in request triggers](#lambda-generating-http-responses-in-requests).

When you're working with the HTTP response, Lambda@Edge does not expose the body that is returned by the origin server to the origin-response trigger. You can generate a static content body by setting it to the desired value, or remove the body inside the function by setting the value to be empty. If you don't update the body field in your function, the original body returned by the origin server is returned back to viewer.

## Access the request body by choosing the include body option
<a name="lambda-include-body-access"></a>

You can opt to have Lambda@Edge expose the body in a request for writable HTTP methods (POST, PUT, DELETE, and so on), so that you can access it in your Lambda function. You can choose read-only access, or you can specify that you'll replace the body.

To enable this option, choose **Include Body** when you create a CloudFront trigger for your function that's for a viewer request or origin request event. For more information, see [Add triggers for a Lambda@Edge function](lambda-edge-add-triggers.md), or to learn about using **Include Body** with your function, see [Lambda@Edge event structure](lambda-event-structure.md).

Scenarios when you might want to use this feature include the following:
+ Processing web forms, like "contact us" forms, without sending customer input data back to origin servers.
+ Gathering web beacon data that's sent by viewer browsers and processing it at the edge.

For sample code, see [Lambda@Edge example functions](lambda-examples.md).

**Note**  
If the request body is large, Lambda@Edge truncates it. For detailed information about the maximum size and truncation, see [Restrictions on the request body with the include body option](lambda-at-edge-function-restrictions.md#lambda-at-edge-restrictions-request-body).

# Lambda@Edge example functions
<a name="lambda-examples"></a>

See the following examples to use Lambda functions with Amazon CloudFront.

**Note**  
If you choose runtime Node.js 18 or later for your Lambda@Edge function, an `index.mjs` file is created for you automatically. To use the following code examples, rename the `index.mjs` file to `index.js` instead.

**Topics**
+ [General examples](#lambda-examples-general-examples)
+ [Generate responses - examples](#lambda-examples-generated-response-examples)
+ [Query strings - examples](#lambda-examples-query-string-examples)
+ [Personalize content by country or device type headers - examples](#lambda-examples-redirecting-examples)
+ [Content-based dynamic origin selection - examples](#lambda-examples-content-based-routing-examples)
+ [Update error statuses - examples](#lambda-examples-update-error-status-examples)
+ [Access the request body - examples](#lambda-examples-access-request-body-examples)

## General examples
<a name="lambda-examples-general-examples"></a>

The following examples show common ways to use Lambda@Edge in CloudFront.

**Topics**
+ [Example: A/B testing](#lambda-examples-a-b-testing)
+ [Example: Override a response header](#lambda-examples-overriding-response-header)

### Example: A/B testing
<a name="lambda-examples-a-b-testing"></a>

You can use the following example to test two different versions of an image without creating redirects or changing the URL. This example reads the cookies in the viewer request and modifies the request URL accordingly. If the viewer doesn't send a cookie with one of the expected values, the example randomly assigns the viewer to one of the URLs.

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    if (request.uri !== '/experiment-pixel.jpg') {
        // do not process if this is not an A-B test request
        callback(null, request);
        return;
    }

    const cookieExperimentA = 'X-Experiment-Name=A';
    const cookieExperimentB = 'X-Experiment-Name=B';
    const pathExperimentA = '/experiment-group/control-pixel.jpg';
    const pathExperimentB = '/experiment-group/treatment-pixel.jpg';

    /*
     * Lambda at the Edge headers are array objects.
     *
     * Client may send multiple Cookie headers, i.e.:
     * > GET /viewerRes/test HTTP/1.1
     * > User-Agent: curl/7.18.1 (x86_64-unknown-linux-gnu) libcurl/7.18.1 OpenSSL/1.0.1u zlib/1.2.3
     * > Cookie: First=1; Second=2
     * > Cookie: ClientCode=abc
     * > Host: example.com
     *
     * You can access the first Cookie header at headers["cookie"][0].value
     * and the second at headers["cookie"][1].value.
     *
     * Header values are not parsed. In the example above,
     * headers["cookie"][0].value is equal to "First=1; Second=2"
     */
    let experimentUri;
    if (headers.cookie) {
        for (let i = 0; i < headers.cookie.length; i++) {
            if (headers.cookie[i].value.indexOf(cookieExperimentA) >= 0) {
                console.log('Experiment A cookie found');
                experimentUri = pathExperimentA;
                break;
            } else if (headers.cookie[i].value.indexOf(cookieExperimentB) >= 0) {
                console.log('Experiment B cookie found');
                experimentUri = pathExperimentB;
                break;
            }
        }
    }

    if (!experimentUri) {
        console.log('Experiment cookie has not been found. Throwing dice...');
        if (Math.random() < 0.75) {
            experimentUri = pathExperimentA;
        } else {
            experimentUri = pathExperimentB;
        }
    }

    request.uri = experimentUri;
    console.log(`Request uri set to "${request.uri}"`);
    callback(null, request);
};
```

------
#### [ Python ]

```
import json
import random

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    if request['uri'] != '/experiment-pixel.jpg':
        # Not an A/B Test
        return request

    cookieExperimentA, cookieExperimentB = 'X-Experiment-Name=A', 'X-Experiment-Name=B'
    pathExperimentA, pathExperimentB = '/experiment-group/control-pixel.jpg', '/experiment-group/treatment-pixel.jpg'

    '''
    Lambda at the Edge headers are array objects.

    Client may send multiple cookie headers. For example:
    > GET /viewerRes/test HTTP/1.1
    > User-Agent: curl/7.18.1 (x86_64-unknown-linux-gnu) libcurl/7.18.1 OpenSSL/1.0.1u zlib/1.2.3
    > Cookie: First=1; Second=2
    > Cookie: ClientCode=abc
    > Host: example.com

    You can access the first Cookie header at headers["cookie"][0].value
    and the second at headers["cookie"][1].value.

    Header values are not parsed. In the example above,
    headers["cookie"][0].value is equal to "First=1; Second=2"
    '''

    experimentUri = ""

    for cookie in headers.get('cookie', []):
        if cookieExperimentA in cookie['value']:
            print("Experiment A cookie found")
            experimentUri = pathExperimentA
            break
        elif cookieExperimentB in cookie['value']:
            print("Experiment B cookie found")
            experimentUri = pathExperimentB
            break

    if not experimentUri:
        print("Experiment cookie has not been found. Throwing dice...")
        if random.random() < 0.75:
            experimentUri = pathExperimentA
        else:
            experimentUri = pathExperimentB

    request['uri'] = experimentUri
    print(f"Request uri set to {experimentUri}")
    return request
```

------

### Example: Override a response header
<a name="lambda-examples-overriding-response-header"></a>

The following example shows how to change the value of a response header based on the value of another header.

------
#### [ Node.js ]

```
export const handler = async (event) => {
    const response = event.Records[0].cf.response;
    const headers = response.headers;

    const headerNameSrc = 'X-Amz-Meta-Last-Modified';
    const headerNameDst = 'Last-Modified';

    if (headers[headerNameSrc.toLowerCase()]) {
        headers[headerNameDst.toLowerCase()] = [{
            key: headerNameDst,
            value: headers[headerNameSrc.toLowerCase()][0].value,
        }];
        console.log(`Response header "${headerNameDst}" was set to ` +
                    `"${headers[headerNameDst.toLowerCase()][0].value}"`);
    }

    return response;
};
```

------
#### [ Python ]

```
import json 

def lambda_handler(event, context):
    response = event['Records'][0]['cf']['response']
    headers = response['headers']
    
    header_name_src = 'X-Amz-Meta-Last-Modified'
    header_name_dst = 'Last-Modified'
    
    if headers.get(header_name_src.lower()):
        headers[header_name_dst.lower()] = [{
            'key': header_name_dst,
            'value': headers[header_name_src.lower()][0]['value']
        }]
        print(f'Response header "{header_name_dst}" was set to '
              f'"{headers[header_name_dst.lower()][0]["value"]}"')
    
    return response
```

------

## Generate responses - examples
<a name="lambda-examples-generated-response-examples"></a>

The following examples show how you can use Lambda@Edge to generate responses.

**Topics**
+ [Example: Serve static content (generated response)](#lambda-examples-static-web-server)
+ [Example: Generate an HTTP redirect (generated response)](#lambda-examples-http-redirect)

### Example: Serve static content (generated response)
<a name="lambda-examples-static-web-server"></a>

The following example shows how to use a Lambda function to serve static website content, which reduces the load on the origin server and reduces overall latency. 

**Note**  
You can generate HTTP responses for viewer request and origin request events. For more information, see [Generate HTTP responses in request triggers](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests).  
You can also replace or remove the body of the HTTP response in origin response events. For more information, see [Update HTTP responses in origin response triggers](lambda-generating-http-responses.md#lambda-updating-http-responses).

------
#### [ Node.js ]

```
'use strict';

const content = `
<\!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Simple Lambda@Edge Static Content Response</title>
  </head>
  <body>
    <p>Hello from Lambda@Edge!</p>
  </body>
</html>
`;

exports.handler = (event, context, callback) => {
    /*
     * Generate HTTP OK response using 200 status code with HTML body.
     */
    const response = {
        status: '200',
        statusDescription: 'OK',
        headers: {
            'cache-control': [{
                key: 'Cache-Control',
                value: 'max-age=100'
            }],
            'content-type': [{
                key: 'Content-Type',
                value: 'text/html'
            }]
        },
        body: content,
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
import json

CONTENT = """
<\!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Simple Lambda@Edge Static Content Response</title>
</head>
<body>
    <p>Hello from Lambda@Edge!</p>
</body>
</html>
"""

def lambda_handler(event, context):
    # Generate HTTP OK response using 200 status code with HTML body.
    response = {
        'status': '200',
        'statusDescription': 'OK',
        'headers': {
            'cache-control': [
                {
                    'key': 'Cache-Control',
                    'value': 'max-age=100'
                }
            ],
            "content-type": [
                {
                    'key': 'Content-Type',
                    'value': 'text/html'
                }
            ]
        },
        'body': CONTENT
    }
    return response
```

------

### Example: Generate an HTTP redirect (generated response)
<a name="lambda-examples-http-redirect"></a>

The following example shows how to generate an HTTP redirect.

**Note**  
You can generate HTTP responses for viewer request and origin request events. For more information, see [Generate HTTP responses in request triggers](lambda-generating-http-responses.md#lambda-generating-http-responses-in-requests).

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    /*
     * Generate HTTP redirect response with 302 status code and Location header.
     */
    const response = {
        status: '302',
        statusDescription: 'Found',
        headers: {
            location: [{
                key: 'Location',
                value: 'https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html',
            }],
        },
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):

    # Generate HTTP redirect response with 302 status code and Location header.

    response = {
        'status': '302',
        'statusDescription': 'Found',
        'headers': {
            'location': [{
                'key': 'Location',
                'value': 'https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html'
            }]
        }
    }

    return response
```

------

## Query strings - examples
<a name="lambda-examples-query-string-examples"></a>

The following examples show ways that you can use Lambda@Edge with query strings.

**Topics**
+ [Example: Add a header based on a query string parameter](#lambda-examples-header-based-on-query-string)
+ [Example: Normalize query string parameters to improve the cache hit ratio](#lambda-examples-normalize-query-string-parameters)
+ [Example: Redirect unauthenticated users to a sign-in page](#lambda-examples-redirect-to-signin-page)

### Example: Add a header based on a query string parameter
<a name="lambda-examples-header-based-on-query-string"></a>

The following example shows how to get the key-value pair of a query string parameter, and then add a header based on those values.

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    
    /* When a request contains a query string key-value pair but the origin server
     * expects the value in a header, you can use this Lambda function to
     * convert the key-value pair to a header. Here's what the function does:
     * 1. Parses the query string and gets the key-value pair.
     * 2. Adds a header to the request using the key-value pair that the function got in step 1.
     */

    /* Parse request querystring to get javascript object */
    const params = querystring.parse(request.querystring);

    /* Move auth param from querystring to headers */
    const headerName = 'Auth-Header';
    request.headers[headerName.toLowerCase()] = [{ key: headerName, value: params.auth }];
    delete params.auth;

    /* Update request querystring */
    request.querystring = querystring.stringify(params);

    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs, urlencode

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    '''
    When a request contains a query string key-value pair but the origin server
    expects the value in a header, you can use this Lambda function to
    convert the key-value pair to a header. Here's what the function does:
        1. Parses the query string and gets the key-value pair.
        2. Adds a header to the request using the key-value pair that the function got in step 1.
    '''

    # Parse request querystring to get dictionary/json
    params = {k : v[0] for k, v in parse_qs(request['querystring']).items()}

    # Move auth param from querystring to headers
    headerName = 'Auth-Header'
    request['headers'][headerName.lower()] = [{'key': headerName, 'value': params['auth']}]
    del params['auth']

    # Update request querystring
    request['querystring'] = urlencode(params)

    return request
```

------

### Example: Normalize query string parameters to improve the cache hit ratio
<a name="lambda-examples-normalize-query-string-parameters"></a>

The following example shows how to improve your cache hit ratio by making the following changes to query strings before CloudFront forwards requests to your origin:
+ Alphabetize key-value pairs by the name of the parameter.
+ Change the case of key-value pairs to lowercase.

For more information, see [Cache content based on query string parameters](QueryStringParameters.md).

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    /* When you configure a distribution to forward query strings to the origin and
     * to cache based on an allowlist of query string parameters, we recommend
     * the following to improve the cache-hit ratio:
     * - Always list parameters in the same order.
     * - Use the same case for parameter names and values.
     *
     * This function normalizes query strings so that parameter names and values
     * are lowercase and parameter names are in alphabetical order.
     *
     * For more information, see:
     * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html
     */

    console.log('Query String: ', request.querystring);

    /* Parse request query string to get javascript object */
    const params = querystring.parse(request.querystring.toLowerCase());
    const sortedParams = {};

    /* Sort param keys */
    Object.keys(params).sort().forEach(key => {
        sortedParams[key] = params[key];
    });

    /* Update request querystring with normalized  */
    request.querystring = querystring.stringify(sortedParams);

    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs, urlencode

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    '''
    When you configure a distribution to forward query strings to the origin and
    to cache based on an allowlist of query string parameters, we recommend
    the following to improve the cache-hit ratio:
    Always list parameters in the same order.
    - Use the same case for parameter names and values.

    This function normalizes query strings so that parameter names and values
    are lowercase and parameter names are in alphabetical order.

    For more information, see:
    https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/QueryStringParameters.html
    '''
    print("Query string: ", request["querystring"])

    # Parse request query string to get js object
    params = {k : v[0] for k, v in parse_qs(request['querystring'].lower()).items()}

    # Sort param keys
    sortedParams = sorted(params.items(), key=lambda x: x[0])

    # Update request querystring with normalized
    request['querystring'] = urlencode(sortedParams)
    
    return request
```

------

### Example: Redirect unauthenticated users to a sign-in page
<a name="lambda-examples-redirect-to-signin-page"></a>

The following example shows how to redirect users to a sign-in page if they haven't entered their credentials.

------
#### [ Node.js ]

```
'use strict';

function parseCookies(headers) {
    const parsedCookie = {};
    if (headers.cookie) {
        headers.cookie[0].value.split(';').forEach((cookie) => {
            if (cookie) {
                const parts = cookie.split('=');
                parsedCookie[parts[0].trim()] = parts[1].trim();
            }
        });
    }
    return parsedCookie;
}

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    /* Check for session-id in request cookie in viewer-request event,
     * if session-id is absent, redirect the user to sign in page with original
     * request sent as redirect_url in query params.
     */

    /* Check for session-id in cookie, if present then proceed with request */
    const parsedCookies = parseCookies(headers);
    if (parsedCookies && parsedCookies['session-id']) {
        callback(null, request);
        return;
    }

    /* URI encode the original request to be sent as redirect_url in query params */
    const encodedRedirectUrl = encodeURIComponent(`https://${headers.host[0].value}${request.uri}?${request.querystring}`);
    const response = {
        status: '302',
        statusDescription: 'Found',
        headers: {
            location: [{
                key: 'Location',
                value: `https://www.example.com/signin?redirect_url=${encodedRedirectUrl}`,
            }],
        },
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
import urllib

def parseCookies(headers):
    parsedCookie = {}
    if headers.get('cookie'):
        for cookie in headers['cookie'][0]['value'].split(';'):
            if cookie:
                parts = cookie.split('=')
                parsedCookie[parts[0].strip()] = parts[1].strip()
    return parsedCookie

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    '''
    Check for session-id in request cookie in viewer-request event,
    if session-id is absent, redirect the user to sign in page with original
    request sent as redirect_url in query params.
    '''

    # Check for session-id in cookie, if present, then proceed with request
    parsedCookies = parseCookies(headers)

    if parsedCookies and parsedCookies['session-id']:
        return request

    # URI encode the original request to be sent as redirect_url in query params
    redirectUrl = "https://%s%s?%s" % (headers['host'][0]['value'], request['uri'], request['querystring'])
    encodedRedirectUrl = urllib.parse.quote_plus(redirectUrl.encode('utf-8'))

    response = {
        'status': '302',
        'statusDescription': 'Found',
        'headers': {
            'location': [{
                'key': 'Location',
                'value': 'https://www.example.com/signin?redirect_url=%s' % encodedRedirectUrl
            }]
        }
    }
    return response
```

------

## Personalize content by country or device type headers - examples
<a name="lambda-examples-redirecting-examples"></a>

The following examples show how you can use Lambda@Edge to customize behavior based on location or the type of device used by the viewer.

**Topics**
+ [Example: Redirect viewer requests to a country-specific URL](#lambda-examples-redirect-based-on-country)
+ [Example: Serve different versions of an object based on the device](#lambda-examples-vary-on-device-type)

### Example: Redirect viewer requests to a country-specific URL
<a name="lambda-examples-redirect-based-on-country"></a>

The following example shows how to generate an HTTP redirect response with a country-specific URL and return the response to the viewer. This is useful when you want to provide country-specific responses. For example:
+ If you have country-specific subdomains, such as us.example.com and tw.example.com, you can generate a redirect response when a viewer requests example.com.
+ If you're streaming video but you don't have rights to stream the content in a specific country, you can redirect users in that country to a page that explains why they can't view the video. 

Note the following:
+ You must configure your distribution to cache based on the `CloudFront-Viewer-Country` header. For more information, see [Cache based on selected request headers](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders).
+ CloudFront adds the `CloudFront-Viewer-Country` header after the viewer request event. To use this example, you must create a trigger for the origin request event.

------
#### [ Node.js ]

```
'use strict';

/* This is an origin request function */
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    /*
     * Based on the value of the CloudFront-Viewer-Country header, generate an
     * HTTP status code 302 (Redirect) response, and return a country-specific
     * URL in the Location header.
     * NOTE: 1. You must configure your distribution to cache based on the
     *          CloudFront-Viewer-Country header. For more information, see
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
     *       2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
     *          request event. To use this example, you must create a trigger for the
     *          origin request event.
     */

    let url = 'https://example.com/';
    if (headers['cloudfront-viewer-country']) {
        const countryCode = headers['cloudfront-viewer-country'][0].value;
        if (countryCode === 'TW') {
            url = 'https://tw.example.com/';
        } else if (countryCode === 'US') {
            url = 'https://us.example.com/';
        }
    }

    const response = {
        status: '302',
        statusDescription: 'Found',
        headers: {
            location: [{
                key: 'Location',
                value: url,
            }],
        },
    };
    callback(null, response);
};
```

------
#### [ Python ]

```
# This is an origin request function

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    '''
    Based on the value of the CloudFront-Viewer-Country header, generate an
    HTTP status code 302 (Redirect) response, and return a country-specific
    URL in the Location header.
    NOTE: 1. You must configure your distribution to cache based on the
            CloudFront-Viewer-Country header. For more information, see
            https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
          2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
            request event. To use this example, you must create a trigger for the
            origin request event.
    '''

    url = 'https://example.com/'
    viewerCountry = headers.get('cloudfront-viewer-country')
    if viewerCountry:
        countryCode = viewerCountry[0]['value']
        if countryCode == 'TW':
            url = 'https://tw.example.com/'
        elif countryCode == 'US':
            url = 'https://us.example.com/'

    response = {
        'status': '302',
        'statusDescription': 'Found',
        'headers': {
            'location': [{
                'key': 'Location',
                'value': url
            }]
        }
    }

    return response
```

------

### Example: Serve different versions of an object based on the device
<a name="lambda-examples-vary-on-device-type"></a>

The following example shows how to serve different versions of an object based on the type of device that the user is using, for example, a mobile device or a tablet. Note the following:
+ You must configure your distribution to cache based on the `CloudFront-Is-*-Viewer` headers. For more information, see [Cache based on selected request headers](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders).
+ CloudFront adds the `CloudFront-Is-*-Viewer` headers after the viewer request event. To use this example, you must create a trigger for the origin request event.

------
#### [ Node.js ]

```
'use strict';

/* This is an origin request function */
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    /*
     * Serve different versions of an object based on the device type.
     * NOTE: 1. You must configure your distribution to cache based on the
     *          CloudFront-Is-*-Viewer headers. For more information, see
     *          the following documentation:
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-device-type
     *       2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer
     *          request event. To use this example, you must create a trigger for the
     *          origin request event.
     */

    const desktopPath = '/desktop';
    const mobilePath = '/mobile';
    const tabletPath = '/tablet';
    const smarttvPath = '/smarttv';

    if (headers['cloudfront-is-desktop-viewer']
        && headers['cloudfront-is-desktop-viewer'][0].value === 'true') {
        request.uri = desktopPath + request.uri;
    } else if (headers['cloudfront-is-mobile-viewer']
               && headers['cloudfront-is-mobile-viewer'][0].value === 'true') {
        request.uri = mobilePath + request.uri;
    } else if (headers['cloudfront-is-tablet-viewer']
               && headers['cloudfront-is-tablet-viewer'][0].value === 'true') {
        request.uri = tabletPath + request.uri;
    } else if (headers['cloudfront-is-smarttv-viewer']
               && headers['cloudfront-is-smarttv-viewer'][0].value === 'true') {
        request.uri = smarttvPath + request.uri;
    }
    console.log(`Request uri set to "${request.uri}"`);

    callback(null, request);
};
```

------
#### [ Python ]

```
# This is an origin request function
def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    headers = request['headers']

    '''
    Serve different versions of an object based on the device type.
    NOTE: 1. You must configure your distribution to cache based on the
            CloudFront-Is-*-Viewer headers. For more information, see
            the following documentation:
            https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
            https://docs.aws.amazon.com/console/cloudfront/cache-on-device-type
          2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer
            request event. To use this example, you must create a trigger for the
            origin request event.
    '''

    desktopPath = '/desktop';
    mobilePath = '/mobile';
    tabletPath = '/tablet';
    smarttvPath = '/smarttv';

    if 'cloudfront-is-desktop-viewer' in headers and headers['cloudfront-is-desktop-viewer'][0]['value'] == 'true':
        request['uri'] = desktopPath + request['uri']
    elif 'cloudfront-is-mobile-viewer' in headers and headers['cloudfront-is-mobile-viewer'][0]['value'] == 'true':
        request['uri'] = mobilePath + request['uri']
    elif 'cloudfront-is-tablet-viewer' in headers and headers['cloudfront-is-tablet-viewer'][0]['value'] == 'true':
        request['uri'] = tabletPath + request['uri']
    elif 'cloudfront-is-smarttv-viewer' in headers and headers['cloudfront-is-smarttv-viewer'][0]['value'] == 'true':
        request['uri'] = smarttvPath + request['uri']

    print("Request uri set to %s" % request['uri'])

    return request
```

------

## Content-based dynamic origin selection - examples
<a name="lambda-examples-content-based-routing-examples"></a>

The following examples show how you can use Lambda@Edge to route to different origins based on information in the request.

**Topics**
+ [Example: Use an origin request trigger to change from a custom origin to an Amazon S3 origin](#lambda-examples-content-based-S3-origin-based-on-query)
+ [Example: Use an origin-request trigger to change the Amazon S3 origin Region](#lambda-examples-content-based-S3-origin-request-trigger)
+ [Example: Use an origin request trigger to change from an Amazon S3 origin to a custom origin](#lambda-examples-content-based-custom-origin-request-trigger)
+ [Example: Use an origin request trigger to gradually transfer traffic from one Amazon S3 bucket to another](#lambda-examples-content-based-gradual-traffic-transfer)
+ [Example: Use an origin request trigger to change the origin domain name based on the country header](#lambda-examples-content-based-geo-header)

### Example: Use an origin request trigger to change from a custom origin to an Amazon S3 origin
<a name="lambda-examples-content-based-S3-origin-based-on-query"></a>

This function demonstrates how an origin-request trigger can be used to change from a custom origin to an Amazon S3 origin from which the content is fetched, based on request properties.

------
#### [ Node.js ]

```
'use strict';

 const querystring = require('querystring');
 
 exports.handler = (event, context, callback) => {
     const request = event.Records[0].cf.request;
 
     /**
      * Reads query string to check if S3 origin should be used, and
      * if true, sets S3 origin properties.
      */
 
     const params = querystring.parse(request.querystring);
 
     if (params['useS3Origin']) {
         if (params['useS3Origin'] === 'true') {
             const s3DomainName = 'amzn-s3-demo-bucket.s3.amazonaws.com';
 
             /* Set S3 origin fields */
             request.origin = {
                 s3: {
                     domainName: s3DomainName,
                     region: '',
                     authMethod: 'origin-access-identity',
                     path: '',
                     customHeaders: {}
                 }
             };
             request.headers['host'] = [{ key: 'host', value: s3DomainName}];
         }
     }
     
    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    '''
    Reads query string to check if S3 origin should be used, and
    if true, sets S3 origin properties
    '''
    params = {k: v[0] for k, v in parse_qs(request['querystring']).items()}
    if params.get('useS3Origin') == 'true':
        s3DomainName = 'amzn-s3-demo-bucket.s3.amazonaws.com'

        # Set S3 origin fields
        request['origin'] = {
            's3': {
                'domainName': s3DomainName,
                'region': '',
                'authMethod': 'origin-access-identity',
                'path': '',
                'customHeaders': {}
            }
        }
        request['headers']['host'] = [{'key': 'host', 'value': s3DomainName}]
    return request
```

------

### Example: Use an origin-request trigger to change the Amazon S3 origin Region
<a name="lambda-examples-content-based-S3-origin-request-trigger"></a>

This function demonstrates how an origin-request trigger can be used to change the Amazon S3 origin from which the content is fetched, based on request properties.

In this example, we use the value of the `CloudFront-Viewer-Country` header to update the S3 bucket domain name to a bucket in a Region that is closer to the viewer. This can be useful in several ways:
+ It reduces latencies when the Region specified is nearer to the viewer's country.
+ It provides data sovereignty by making sure that data is served from an origin that's in the same country that the request came from.

To use this example, you must do the following:
+ Configure your distribution to cache based on the `CloudFront-Viewer-Country` header. For more information, see [Cache based on selected request headers](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders). 
+ Create a trigger for this function in the origin request event. CloudFront adds the `CloudFront-Viewer-Country` header after the viewer request event, so to use this example, you must make sure that the function executes for an origin request.

**Note**  
The following example code uses the same origin access identity (OAI) for all S3 buckets that you're using for your origin. For more information, see [Origin access identity](private-content-restricting-access-to-s3.md#private-content-restricting-access-to-s3-oai).

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;

    /**
     * This blueprint demonstrates how an origin-request trigger can be used to
     * change the origin from which the content is fetched, based on request properties.
     * In this example, we use the value of the CloudFront-Viewer-Country header
     * to update the S3 bucket domain name to a bucket in a Region that is closer to
     * the viewer.
     * 
     * This can be useful in several ways:
     *      1) Reduces latencies when the Region specified is nearer to the viewer's
     *         country.
     *      2) Provides data sovereignty by making sure that data is served from an
     *         origin that's in the same country that the request came from.
     * 
     * NOTE: 1. You must configure your distribution to cache based on the
     *          CloudFront-Viewer-Country header. For more information, see
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
     *       2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
     *          request event. To use this example, you must create a trigger for the
     *          origin request event.
     */

    const countryToRegion = {
        'DE': 'eu-central-1',
        'IE': 'eu-west-1',
        'GB': 'eu-west-2',
        'FR': 'eu-west-3',
        'JP': 'ap-northeast-1',
        'IN': 'ap-south-1'
    };

    if (request.headers['cloudfront-viewer-country']) {
        const countryCode = request.headers['cloudfront-viewer-country'][0].value;
        const region = countryToRegion[countryCode];
        
        /**
         * If the viewer's country is not in the list you specify, the request
         * goes to the default S3 bucket you've configured.
         */  
        if (region) {
            /**
             * If you've set up OAI, the bucket policy in the destination bucket
             * should allow the OAI GetObject operation, as configured by default
             * for an S3 origin with OAI. Another requirement with OAI is to provide
             * the Region so it can be used for the SIGV4 signature. Otherwise, the
             * Region is not required.
             */
            request.origin.s3.region = region;
            const domainName = `amzn-s3-demo-bucket-in-${region}.s3.${region}.amazonaws.com`;
            request.origin.s3.domainName = domainName;
            request.headers['host'] = [{ key: 'host', value: domainName }];
        }
    }

    callback(null, request);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    '''
    This blueprint demonstrates how an origin-request trigger can be used to
    change the origin from which the content is fetched, based on request properties.
    In this example, we use the value of the CloudFront-Viewer-Country header
    to update the S3 bucket domain name to a bucket in a Region that is closer to
    the viewer.
    
    This can be useful in several ways:
        1) Reduces latencies when the Region specified is nearer to the viewer's
            country.
        2) Provides data sovereignty by making sure that data is served from an
            origin that's in the same country that the request came from.
    
    NOTE: 1. You must configure your distribution to cache based on the
            CloudFront-Viewer-Country header. For more information, see
            https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
          2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
            request event. To use this example, you must create a trigger for the
            origin request event.
    '''

    countryToRegion = {
        'DE': 'eu-central-1',
        'IE': 'eu-west-1',
        'GB': 'eu-west-2',
        'FR': 'eu-west-3',
        'JP': 'ap-northeast-1',
        'IN': 'ap-south-1'
    }

    viewerCountry = request['headers'].get('cloudfront-viewer-country')
    if viewerCountry:
        countryCode = viewerCountry[0]['value']
        region = countryToRegion.get(countryCode)

        # If the viewer's country in not in the list you specify, the request
        # goes to the default S3 bucket you've configured
        if region:
            '''
            If you've set up OAI, the bucket policy in the destination bucket
            should allow the OAI GetObject operation, as configured by default
            for an S3 origin with OAI. Another requirement with OAI is to provide
            the Region so it can be used for the SIGV4 signature. Otherwise, the
            Region is not required.
            '''
            request['origin']['s3']['region'] = region
            domainName = 'amzn-s3-demo-bucket-in-{0}.s3.{0}.amazonaws.com'.format(region)
            request['origin']['s3']['domainName'] = domainName
            request['headers']['host'] = [{'key': 'host', 'value': domainName}]

    return request
```

------

### Example: Use an origin request trigger to change from an Amazon S3 origin to a custom origin
<a name="lambda-examples-content-based-custom-origin-request-trigger"></a>

This function demonstrates how an origin-request trigger can be used to change the custom origin from which the content is fetched, based on request properties.

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');
 
 exports.handler = (event, context, callback) => {
     const request = event.Records[0].cf.request;
 
     /**
      * Reads query string to check if custom origin should be used, and
      * if true, sets custom origin properties.
      */
 
     const params = querystring.parse(request.querystring);
 
     if (params['useCustomOrigin']) {
         if (params['useCustomOrigin'] === 'true') {
 
             /* Set custom origin fields*/
             request.origin = {
                 custom: {
                     domainName: 'www.example.com',
                     port: 443,
                     protocol: 'https',
                     path: '',
                     sslProtocols: ['TLSv1', 'TLSv1.1'],
                     readTimeout: 5,
                     keepaliveTimeout: 5,
                     customHeaders: {}
                 }
             };
             request.headers['host'] = [{ key: 'host', value: 'www.example.com'}];
         }
     }
    callback(null, request);
};
```

------
#### [ Python ]

```
from urllib.parse import parse_qs

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    # Reads query string to check if custom origin should be used, and
    # if true, sets custom origin properties

    params = {k: v[0] for k, v in parse_qs(request['querystring']).items()}

    if params.get('useCustomOrigin') == 'true':
            # Set custom origin fields
            request['origin'] = {
                'custom': {
                    'domainName': 'www.example.com',
                    'port': 443,
                    'protocol': 'https',
                    'path': '',
                    'sslProtocols': ['TLSv1', 'TLSv1.1'],
                    'readTimeout': 5,
                    'keepaliveTimeout': 5,
                    'customHeaders': {}
                }
            }
            request['headers']['host'] = [{'key': 'host', 'value': 'www.example.com'}]

    return request
```

------

### Example: Use an origin request trigger to gradually transfer traffic from one Amazon S3 bucket to another
<a name="lambda-examples-content-based-gradual-traffic-transfer"></a>

This function demonstrates how you can gradually transfer traffic from one Amazon S3 bucket to another in a controlled way.

------
#### [ Node.js ]

```
'use strict';

    function getRandomInt(min, max) {
        /* Random number is inclusive of min and max*/
        return Math.floor(Math.random() * (max - min + 1)) + min;
 }

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const BLUE_TRAFFIC_PERCENTAGE = 80;

    /**
      * This Lambda function demonstrates how to gradually transfer traffic from
      * one S3 bucket to another in a controlled way.
      * We define a variable BLUE_TRAFFIC_PERCENTAGE which can take values from
      * 1 to 100. If the generated randomNumber less than or equal to BLUE_TRAFFIC_PERCENTAGE, traffic
      * is re-directed to blue-bucket. If not, the default bucket that we've configured
      * is used.
      */

    const randomNumber = getRandomInt(1, 100);

if (randomNumber <= BLUE_TRAFFIC_PERCENTAGE) {
         const domainName = 'blue-bucket.s3.amazonaws.com';
         request.origin.s3.domainName = domainName;
         request.headers['host'] = [{ key: 'host', value: domainName}];
     }
    callback(null, request);
};
```

------
#### [ Python ]

```
import math
import random

def getRandomInt(min, max):
    # Random number is inclusive of min and max
    return math.floor(random.random() * (max - min + 1)) + min

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    BLUE_TRAFFIC_PERCENTAGE = 80

    '''
    This Lambda function demonstrates how to gradually transfer traffic from
    one S3 bucket to another in a controlled way.
    We define a variable BLUE_TRAFFIC_PERCENTAGE which can take values from
    1 to 100. If the generated randomNumber less than or equal to BLUE_TRAFFIC_PERCENTAGE, traffic
    is re-directed to blue-bucket. If not, the default bucket that we've configured
    is used.
    '''

    randomNumber = getRandomInt(1, 100)

    if randomNumber <= BLUE_TRAFFIC_PERCENTAGE:
        domainName = 'blue-bucket.s3.amazonaws.com'
        request['origin']['s3']['domainName'] = domainName
        request['headers']['host'] = [{'key': 'host', 'value': domainName}]

    return request
```

------

### Example: Use an origin request trigger to change the origin domain name based on the country header
<a name="lambda-examples-content-based-geo-header"></a>

This function demonstrates how you can change the origin domain name based on the `CloudFront-Viewer-Country` header, so content is served from an origin closer to the viewer's country.

Implementing this functionality for your distribution can have advantages such as the following:
+ Reducing latencies when the Region specified is nearer to the viewer's country
+ Providing data sovereignty by making sure that data is served from an origin that's in the same country that the request came from

Note that to enable this functionality you must configure your distribution to cache based on the `CloudFront-Viewer-Country` header. For more information, see [Cache based on selected request headers](DownloadDistValuesCacheBehavior.md#DownloadDistValuesForwardHeaders).

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
     const request = event.Records[0].cf.request;
     
  if (request.headers['cloudfront-viewer-country']) {
         const countryCode = request.headers['cloudfront-viewer-country'][0].value;
         if (countryCode === 'GB' || countryCode === 'DE' || countryCode === 'IE' ) {
             const domainName = 'eu.example.com';
             request.origin.custom.domainName = domainName;
             request.headers['host'] = [{key: 'host', value: domainName}];
         } 
     }
     
    callback(null, request);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    viewerCountry = request['headers'].get('cloudfront-viewer-country')
    if viewerCountry:
        countryCode = viewerCountry[0]['value']
        if countryCode == 'GB' or countryCode == 'DE' or countryCode == 'IE':
            domainName = 'eu.example.com'
            request['origin']['custom']['domainName'] = domainName
            request['headers']['host'] = [{'key': 'host', 'value': domainName}]
    return request
```

------

## Update error statuses - examples
<a name="lambda-examples-update-error-status-examples"></a>

The following examples provide guidance for how you can use Lambda@Edge to change the error status that is returned to users.

**Topics**
+ [Example: Use an origin response trigger to update the error status code to 200](#lambda-examples-custom-error-static-body)
+ [Example: Use an origin response trigger to update the error status code to 302](#lambda-examples-custom-error-new-site)

### Example: Use an origin response trigger to update the error status code to 200
<a name="lambda-examples-custom-error-static-body"></a>

This function demonstrates how you can update the response status to 200 and generate static body content to return to the viewer in the following scenario:
+ The function is triggered in an origin response.
+ The response status from the origin server is an error status code (4xx or 5xx).

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;

    /**
     * This function updates the response status to 200 and generates static
     * body content to return to the viewer in the following scenario:
     * 1. The function is triggered in an origin response
     * 2. The response status from the origin server is an error status code (4xx or 5xx)
     */

    if (response.status >= 400 && response.status <= 599) {
        response.status = 200;
        response.statusDescription = 'OK';
        response.body = 'Body generation example';
    }

    callback(null, response);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    response = event['Records'][0]['cf']['response']

    '''
    This function updates the response status to 200 and generates static
    body content to return to the viewer in the following scenario:
    1. The function is triggered in an origin response
    2. The response status from the origin server is an error status code (4xx or 5xx)
    '''

    if int(response['status']) >= 400 and int(response['status']) <= 599:
        response['status'] = 200
        response['statusDescription'] = 'OK'
        response['body'] = 'Body generation example'
    return response
```

------

### Example: Use an origin response trigger to update the error status code to 302
<a name="lambda-examples-custom-error-new-site"></a>

This function demonstrates how you can update the HTTP status code to 302 to redirect to another path (cache behavior) that has a different origin configured. Note the following:
+ The function is triggered in an origin response.
+ The response status from the origin server is an error status code (4xx or 5xx).

------
#### [ Node.js ]

```
'use strict';

exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;
    const request = event.Records[0].cf.request;

    /**
     * This function updates the HTTP status code in the response to 302, to redirect to another
     * path (cache behavior) that has a different origin configured. Note the following:
     * 1. The function is triggered in an origin response
     * 2. The response status from the origin server is an error status code (4xx or 5xx)
     */

    if (response.status >= 400 && response.status <= 599) {
        const redirect_path = `/plan-b/path?${request.querystring}`;

        response.status = 302;
        response.statusDescription = 'Found';

        /* Drop the body, as it is not required for redirects */
        response.body = '';
        response.headers['location'] = [{ key: 'Location', value: redirect_path }];
    }

    callback(null, response);
};
```

------
#### [ Python ]

```
def lambda_handler(event, context):
    response = event['Records'][0]['cf']['response']
    request = event['Records'][0]['cf']['request']

    '''
    This function updates the HTTP status code in the response to 302, to redirect to another
    path (cache behavior) that has a different origin configured. Note the following:
    1. The function is triggered in an origin response
    2. The response status from the origin server is an error status code (4xx or 5xx)
    '''

    if int(response['status']) >= 400 and int(response['status']) <= 599:
        redirect_path = '/plan-b/path?%s' % request['querystring']

        response['status'] = 302
        response['statusDescription'] = 'Found'

        # Drop the body as it is not required for redirects
        response['body'] = ''
        response['headers']['location'] = [{'key': 'Location', 'value': redirect_path}]

    return response
```

------

## Access the request body - examples
<a name="lambda-examples-access-request-body-examples"></a>

The following examples show how you can use Lambda@Edge to work with POST requests.

**Note**  
To use these examples, you must enable the *include body* option in the distribution's Lambda function association. It is not enabled by default.  
To enable this setting in the CloudFront console, select the check box for **Include Body** in the **Lambda Function Association**.
To enable this setting in the CloudFront API or with CloudFormation, set the `IncludeBody` field to `true` in `LambdaFunctionAssociation`.

**Topics**
+ [Example: Use a request trigger to read an HTML form](#lambda-examples-access-request-body-examples-read)
+ [Example: Use a request trigger to modify an HTML form](#lambda-examples-access-request-body-examples-replace)

### Example: Use a request trigger to read an HTML form
<a name="lambda-examples-access-request-body-examples-read"></a>

This function demonstrates how you can process the body of a POST request generated by an HTML form (web form), such as a "contact us" form. For example, you might have an HTML form like the following:

```
<html>
  <form action="https://example.com" method="post">
    Param 1: <input type="text" name="name1"><br>
    Param 2: <input type="text" name="name2"><br>
    input type="submit" value="Submit">
  </form>
</html>
```

For the example function that follows, the function must be triggered in a CloudFront viewer request or origin request.

------
#### [ Node.js ]

```
'use strict';

const querystring = require('querystring');

/**
 * This function demonstrates how you can read the body of a POST request 
 * generated by an HTML form (web form). The function is triggered in a
 * CloudFront viewer request or origin request event type.
 */

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;

    if (request.method === 'POST') {
        /* HTTP body is always passed as base64-encoded string. Decode it. */
        const body = Buffer.from(request.body.data, 'base64').toString();
 
        /* HTML forms send the data in query string format. Parse it. */
        const params = querystring.parse(body);
 
        /* For demonstration purposes, we only log the form fields here.
         * You can put your custom logic here. For example, you can store the 
         * fields in a database, such as Amazon DynamoDB, and generate a response
         * right from your Lambda@Edge function.
         */
        for (let param in params) {
            console.log(`For "${param}" user submitted "${params[param]}".\n`);
        }
    }
    return callback(null, request);
};
```

------
#### [ Python ]

```
import base64
from urllib.parse import parse_qs

'''
Say there is a POST request body generated by an HTML such as:

<html>
<form action="https://example.com" method="post">
    Param 1: <input type="text" name="name1"><br>
    Param 2: <input type="text" name="name2"><br>
    input type="submit" value="Submit">
</form>
</html>

'''

'''
This function demonstrates how you can read the body of a POST request 
generated by an HTML form (web form). The function is triggered in a
CloudFront viewer request or origin request event type.
'''

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']

    if request['method'] == 'POST':
        # HTTP body is always passed as base64-encoded string. Decode it
        body = base64.b64decode(request['body']['data'])

        # HTML forms send the data in query string format. Parse it
        params = {k: v[0] for k, v in parse_qs(body).items()}

        '''
        For demonstration purposes, we only log the form fields here.
        You can put your custom logic here. For example, you can store the
        fields in a database, such as Amazon DynamoDB, and generate a response
        right from your Lambda@Edge function.
        '''
        for key, value in params.items():
            print("For %s use submitted %s" % (key, value))
            
    return request
```

------

### Example: Use a request trigger to modify an HTML form
<a name="lambda-examples-access-request-body-examples-replace"></a>

This function demonstrates how you can modify the body of a POST request generated by an HTML form (web form). The function is triggered in a CloudFront viewer request or origin request.

------
#### [ Node.js ]

```
'use strict';
				
const querystring = require('querystring');

exports.handler = (event, context, callback) => {
    var request = event.Records[0].cf.request;
    if (request.method === 'POST') {
        /* Request body is being replaced. To do this, update the following
        /* three fields:
         *    1) body.action to 'replace'
         *    2) body.encoding to the encoding of the new data.
         *
         *       Set to one of the following values:
         *
         *           text - denotes that the generated body is in text format.
         *               Lambda@Edge will propagate this as is.
         *           base64 - denotes that the generated body is base64 encoded.
         *               Lambda@Edge will base64 decode the data before sending
         *               it to the origin.
         *    3) body.data to the new body.
         */
        request.body.action = 'replace';
        request.body.encoding = 'text';
        request.body.data = getUpdatedBody(request);
    }
    callback(null, request);
};

function getUpdatedBody(request) {
    /* HTTP body is always passed as base64-encoded string. Decode it. */
    const body = Buffer.from(request.body.data, 'base64').toString();

    /* HTML forms send data in query string format. Parse it. */
    const params = querystring.parse(body);

    /* For demonstration purposes, we're adding one more param.
     *
     * You can put your custom logic here. For example, you can truncate long
     * bodies from malicious requests.
     */
    params['new-param-name'] = 'new-param-value';
    return querystring.stringify(params);
}
```

------
#### [ Python ]

```
import base64
from urllib.parse import parse_qs, urlencode

def lambda_handler(event, context):
    request = event['Records'][0]['cf']['request']
    if request['method'] == 'POST':
        '''
        Request body is being replaced. To do this, update the following
        three fields:
            1) body.action to 'replace'
            2) body.encoding to the encoding of the new data.
        
            Set to one of the following values:
        
                text - denotes that the generated body is in text format.
                    Lambda@Edge will propagate this as is.
                base64 - denotes that the generated body is base64 encoded.
                    Lambda@Edge will base64 decode the data before sending
                    it to the origin.
            3) body.data to the new body.
        '''
        request['body']['action'] = 'replace'
        request['body']['encoding'] = 'text'
        request['body']['data'] = getUpdatedBody(request)
    return request

def getUpdatedBody(request):
    # HTTP body is always passed as base64-encoded string. Decode it
    body = base64.b64decode(request['body']['data'])

    # HTML forms send data in query string format. Parse it
    params = {k: v[0] for k, v in parse_qs(body).items()}

    # For demonstration purposes, we're adding one more param

    # You can put your custom logic here. For example, you can truncate long
    # bodies from malicious requests
    params['new-param-name'] = 'new-param-value'
    return urlencode(params)
```

------