

# Customize at the edge with functions
<a name="edge-functions"></a>

With Amazon CloudFront, you can write your own code to customize how your CloudFront distributions process HTTP requests and responses. The code runs close to your viewers (users) to minimize latency, and you don’t have to manage servers or other infrastructure. You can write code to manipulate the requests and responses that flow through CloudFront, perform basic authentication and authorization, generate HTTP responses at the edge, and more.

The code that you write and attach to your CloudFront distribution is called an *edge function*. CloudFront provides two ways to write and manage edge functions:

**CloudFront Functions**  
You can write lightweight functions in JavaScript for high-scale, latency-sensitive CDN customizations. The CloudFront Functions runtime environment offers submillisecond startup times, scales immediately to handle millions of requests per second, and is highly secure. CloudFront Functions is a native feature of CloudFront, which means you can build, test, and deploy your code entirely within CloudFront.

**Lambda@Edge**  
Lambda@Edge is an extension of [AWS Lambda](https://aws.amazon.com/lambda/) that offers powerful and flexible computing for complex functions and full application logic closer to your viewers, and is highly secure. Lambda@Edge functions run in a Node.js or Python runtime environment. You publish them to a single AWS Region, but when you associate the function with a CloudFront distribution, Lambda@Edge automatically replicates your code around the world.

If you run AWS WAF on CloudFront, you can use AWS WAF inserted headers for both CloudFront Functions and Lambda@Edge. This works for viewer and origin requests and responses.

**Topics**
+ [

# Differences between CloudFront Functions and Lambda@Edge
](edge-functions-choosing.md)
+ [

# Customize at the edge with CloudFront Functions
](cloudfront-functions.md)
+ [

# Customize with CloudFront Connection Functions
](customize-connections-validation-with-connection-functions.md)
+ [

# Customize at the edge with Lambda@Edge
](lambda-at-the-edge.md)
+ [

# Restrictions on edge functions
](edge-functions-restrictions.md)

# Differences between CloudFront Functions and Lambda@Edge
<a name="edge-functions-choosing"></a>

CloudFront Functions and Lambda@Edge both provide a way to run code in response to CloudFront events. 

CloudFront Functions is ideal for lightweight, short-running functions for the following use cases:
+ **Cache key normalization** – Transform HTTP request attributes (headers, query strings, cookies, and even the URL path) to create an optimal [cache key](understanding-the-cache-key.md), which can improve your cache hit ratio.
+ **Header manipulation** – Insert, modify, or delete HTTP headers in the request or response. For example, you can add a `True-Client-IP` header to every request.
+ **URL redirects or rewrites** – Redirect viewers to other pages based on information in the request, or rewrite all requests from one path to another.
+ **Request authorization** – Validate hashed authorization tokens, such as JSON web tokens (JWT), by inspecting authorization headers or other request metadata.

To get started with CloudFront Functions, see [Customize at the edge with CloudFront Functions](cloudfront-functions.md).

Lambda@Edge is ideal for the following use cases:
+ Functions that take several milliseconds or more to complete
+ Functions that require adjustable CPU or memory
+ Functions that depend on third-party libraries (including the AWS SDK, for integration with other AWS services)
+ Functions that require network access to use external services for processing
+ Functions that require file system access or access to the body of HTTP requests

To get started with Lambda@Edge, see [Customize at the edge with Lambda@Edge](lambda-at-the-edge.md).

To help you choose the option for your use case, use the following table to understand the differences between CloudFront Functions and Lambda@Edge. For information about differences that apply for origin modification helper methods, see [Choose between CloudFront Functions and Lambda@Edge](helper-functions-origin-modification.md#origin-modification-considerations).


|  | CloudFront Functions | Lambda@Edge | 
| --- | --- | --- | 
| Programming languages | JavaScript (ECMAScript 5.1 compliant) | Node.js and Python | 
| Event sources |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-functions-choosing.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-functions-choosing.html)  | 
|  Supports [Amazon CloudFront KeyValueStore](kvs-with-functions.md)  |  Yes CloudFront KeyValueStore only supports [JavaScript runtime 2.0](functions-javascript-runtime-20.md)  |  No  | 
| Scale | Up to millions of requests per second | Up to 10,000 requests per second per Region | 
| Function duration | Submillisecond |  Up to 30 seconds (viewer request and viewer response) Up to 30 seconds (origin request and origin response)  | 
|  Maximum function memory size  | 2 MB |  128 MB (viewer request and viewer response) 10,240 MB (10 GB) (origin request and origin response) For more information, see [Quotas on Lambda@Edge](cloudfront-limits.md#limits-lambda-at-edge).  | 
| Maximum size of the function code and included libraries | 10 KB |  50 MB (viewer request and viewer response) 50 MB (origin request and origin response)  | 
| Network access | No | Yes | 
| File system access | No | Yes | 
| Access to the request body | No | Yes | 
| Access to geolocation and device data | Yes |  No (viewer request and viewer response) Yes (origin request and origin response)  | 
| Can build and test entirely within CloudFront | Yes | No | 
| Function logging and metrics | Yes | Yes | 

# Customize at the edge with CloudFront Functions
<a name="cloudfront-functions"></a>

With CloudFront Functions, you can write lightweight functions in JavaScript for high-scale, latency-sensitive CDN customizations. Your functions can manipulate the requests and responses that flow through CloudFront, perform basic authentication and authorization, generate HTTP responses at the edge, and more. The CloudFront Functions runtime environment offers submillisecond startup times, scales immediately to handle millions of requests per second, and is highly secure. CloudFront Functions is a native feature of CloudFront, which means you can build, test, and deploy your code entirely within CloudFront.

When you associate a CloudFront function with a CloudFront distribution, CloudFront intercepts requests and responses at CloudFront edge locations and passes them to your function. You can invoke CloudFront Functions when the following events occur:
+ When CloudFront receives a request from a viewer (viewer request)
+ Before CloudFront returns the response to the viewer (viewer response)
+ During TLS connection establishment (connection request) - currently available for mutual TLS (mTLS) connections

For more information about CloudFront Functions, see the following topics:

**Topics**
+ [

# Tutorial: Create a simple function with CloudFront Functions
](functions-tutorial.md)
+ [

# Tutorial: Create a CloudFront function that includes key values
](functions-tutorial-kvs.md)
+ [

# Write function code
](writing-function-code.md)
+ [

# Create functions
](create-function.md)
+ [

# Test functions
](test-function.md)
+ [

# Update functions
](update-function.md)
+ [

# Publish functions
](publish-function.md)
+ [

# Associate functions with distributions
](associate-function.md)
+ [

# Amazon CloudFront KeyValueStore
](kvs-with-functions.md)

# Tutorial: Create a simple function with CloudFront Functions
<a name="functions-tutorial"></a>

This tutorial shows you how to get started with CloudFront Functions. You can create a simple function that redirects the viewer to a different URL, and that also returns a custom response header.

**Contents**
+ [

## Prerequisites
](#functions-tutorial-prerequisites)
+ [

## Create the function
](#functions-tutorial-create)
+ [

## Verify the function
](#functions-tutorial-verify)

## Prerequisites
<a name="functions-tutorial-prerequisites"></a>

To use CloudFront Functions, you need a CloudFront distribution. If you don’t have one, see [Get started with a CloudFront standard distribution](GettingStarted.SimpleDistribution.md).

## Create the function
<a name="functions-tutorial-create"></a>

You can use the CloudFront console to create a simple function that redirects the viewer to a different URL, and also returns a custom response header. 

**To create a CloudFront function**

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. In the navigation pane, choose **Functions**, and then choose **Create function**. 

1. On the **Create function** page, for **Name**, enter a function name such as *MyFunctionName*.

1. (Optional) For **Description**, enter a description for the function such as **Simple test function**.

1. For **Runtime**, keep the default selected JavaScript version.

1. Choose **Create function**.

1. Copy the following function code. This function code redirects the viewer to a different URL, and also returns a custom response header.

   ```
   function handler(event) {
       // NOTE: This example function is for a viewer request event trigger. 
       // Choose viewer request for event trigger when you associate this function with a distribution. 
       var response = {
           statusCode: 302,
           statusDescription: 'Found',
           headers: {
               'cloudfront-functions': { value: 'generated-by-CloudFront-Functions' },
               'location': { value: 'https://aws.amazon.com/cloudfront/' }
           }
       };
       return response;
   }
   ```

1. For **Function code**, paste the code into the code editor to replace the default code.

1. Choose **Save changes**.

1. (Optional) You can test the function before you publish it. This tutorial doesn’t describe how to test a function. For more information, see [Test functions](test-function.md).

1. Choose the **Publish** tab and then choose **Publish function**. You *must* publish the function before you can associate it with your CloudFront distribution.

1. Next, you can associate the function with a distribution or cache behavior. On the *MyFunctionName* page, choose the **Publish** tab. 
**Warning**  
In the following steps, choose a distribution or a cache behavior that’s used for testing. Don’t associate this test function with a distribution or cache behavior that’s used in production.

1. Choose **Add association**. 

1. On the **Associate** dialog box, choose a distribution and/or a cache behavior. For **Event type**, keep the default value.

1. Choose **Add association**.

   The **Associated distributions** table shows the associated distribution. 

1. Wait a few minutes for the associated distribution to finish deploying. To check the distribution’s status, select the distribution in the **Associated distributions** table and then choose **View distribution**.

   When the distribution’s status is **Deployed**, you’re ready to verify that the function works.

## Verify the function
<a name="functions-tutorial-verify"></a>

After you deploy the function, you can verify that it's working for your distribution.

**To verify the function**

1. In your web browser, navigate to your distribution’s domain name (for example, `https://d111111abcdef8.cloudfront.net`).

   The function returns a redirect to the browser, so the browser automatically goes to `https://aws.amazon.com/cloudfront/`.

1. In a command line window, you can use a tool like **curl** to send a request to your distribution’s domain name.

   ```
   curl -v https://d111111abcdef8.cloudfront.net/
   ```

   In the response, you see the redirect response (`302 Found`) and the custom response headers that the function added. Your response might look like the following example.  
**Example**  

   ```
   curl -v https://d111111abcdef8.cloudfront.net/
   > GET / HTTP/1.1
   > Host: d111111abcdef8.cloudfront.net
   > User-Agent: curl/7.64.1
   > Accept: */*
   >
   < HTTP/1.1 302 Found
   < Server: CloudFront
   < Date: Tue, 16 Mar 2021 18:50:48 GMT
   < Content-Length: 0
   < Connection: keep-alive
   < Location: https://aws.amazon.com/cloudfront/
   < Cloudfront-Functions: generated-by-CloudFront-Functions
   < X-Cache: FunctionGeneratedResponse from cloudfront
   < Via: 1.1 3035b31bddaf14eded329f8d22cf188c.cloudfront.net (CloudFront)
   < X-Amz-Cf-Pop: PHX50-C2
   < X-Amz-Cf-Id: ULZdIz6j43uGBlXyob_JctF9x7CCbwpNniiMlmNbmwzH1YWP9FsEHg==
   ```

# Tutorial: Create a CloudFront function that includes key values
<a name="functions-tutorial-kvs"></a>

This tutorial shows you how to include key values with CloudFront function. Key values are part of a key-value pair. You include the name (from the key-value pair) in the function code. When the function runs, CloudFront replaces the name with the value. 

Key-value pairs are variables that are stored in a key value store. When you use a key in your function (instead of hard-coded values), your function is more flexible. You can change the value of the key without having to deploy code changes. Key-value pairs can also reduce the size of your function. For more information, see [Amazon CloudFront KeyValueStore](kvs-with-functions.md).

**Contents**
+ [

## Prerequisites
](#functions-kvs-tutorial-prerequisites)
+ [

## Create the key value store
](#functions-kvs-tutorial-kvs-step)
+ [

## Add key-value pairs to the key value store
](#add-key-value-pairs-to-store)
+ [

## Associate the key value store with the function
](#functions-kvs-tutorial-functions-step)
+ [

## Test and publish the function code
](#test-and-publish-function-code)

## Prerequisites
<a name="functions-kvs-tutorial-prerequisites"></a>

If you're new to CloudFront Functions functions and the key value store, we recommend that you follow the tutorial in [Tutorial: Create a simple function with CloudFront Functions](functions-tutorial.md).

After you complete that tutorial, you can follow this tutorial to extend the function that you created. For this tutorial, we recommend that you create the key value store first. 

## Create the key value store
<a name="functions-kvs-tutorial-kvs-step"></a>

First, create the key value store to use for your function.

**To create the key value store**

1. Plan the key-value pairs you want to include in the function. Make a note of key names. The key-value pairs that you want to use in a function must be in a single key value store. 

1. Decide about the order of work. There are two ways to proceed:
   + Create a key value store, and add key-value pairs to the store. Then create (or modify) the function and incorporate the key names.
   + Or, create (or modify) the function and incorporate the key names you want to use. Then create a key value store, and add the key-value pairs.

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. In the navigation pane, choose **Functions**, and then choose the **KeyValueStores** tab.

1. Choose **Create KeyValueStore** and enter the following fields:
   + Enter a name and (optional) description for the store. 
   + Leave **S3 URI** blank. In this tutorial you will enter the key-value pairs manually. 

1. Choose **Create**. The details page for the new key value store appears. This page includes a **Key value pairs** section that is currently empty.

## Add key-value pairs to the key value store
<a name="add-key-value-pairs-to-store"></a>

Next, manually add a list of key-value pairs to the key value store that you previously created.

**To add key-value pairs to the key value store**

1. In the **Key value pairs** section, choose **Add key value pairs**. 

1. Choose **Add pair** and then enter a key and value. Choose the check mark to confirm your changes and repeat this step to add more.

1. When you're finished, choose **Save changes** to save the key-value pairs in the key value store. On the confirmation dialog, choose **Done**.

You now have a key value store that contains a group of key-value pairs. 



## Associate the key value store with the function
<a name="functions-kvs-tutorial-functions-step"></a>

You have now created the key value store. And you have created or modified a function that includes the key names from the key value store. You can now associate the key value store and the function. You create that association from within the function. 

**To associate the key value store with the function**

1. In the navigation pane, choose **Functions**. The **Functions** tab appears on top, by default. 

1. Choose the function name and in the **Associated KeyValueStore** section, choose **Associate Existing KeyValueStore**.

1. Select the key value store and choose **Associate KeyValueStore**. 

**Note**  
You can associate only one key value store with each function.

## Test and publish the function code
<a name="test-and-publish-function-code"></a>

After you associate the key value store with your function, you can test and publish the function code. You should always test the function code every time you modify it, including when you do the following:
+ Associate a key value store with the function.
+ Modify the function and its key value store to include a new key-value pair.
+ Change the value of a key-value pair.

**To test and publish the function code**

1. For information about how to test a function, see [Test functions](test-function.md). Make sure that you choose to test the function in the `DEVELOPMENT` stage.

1. Publish the function when you're ready to use the function (with the new or revised key value pairs) in a `LIVE` environment. 

   When you publish, CloudFront copies the version of the function from the `DEVELOPMENT` stage over to the live stage. The function has the new code and is associated with the key value store. (There is no need to perform the association again, in the live stage.)

   For information about how to publish the function, see [Publish functions](publish-function.md). 

# Write function code
<a name="writing-function-code"></a>

You can use CloudFront Functions to write lightweight functions in JavaScript for high-scale, latency-sensitive CDN customizations. Your function code can manipulate the requests and responses that flow through CloudFront, perform basic authentication and authorization, generate HTTP responses at the edge, and more.

To help you write function code for CloudFront Functions, see the following topics. For code examples, see [CloudFront Functions examples for CloudFront](service_code_examples_cloudfront_functions_examples.md) and the [amazon-cloudfront-functions repository](https://github.com/aws-samples/amazon-cloudfront-functions) on GitHub.

**Topics**
+ [

# Determine function purpose
](function-code-choose-purpose.md)
+ [Event structure](functions-event-structure.md)
+ [JavaScript runtime features](functions-javascript-runtime-features.md)
+ [

# Helper methods for key value stores
](functions-custom-methods.md)
+ [

# Helper methods for origin modification
](helper-functions-origin-modification.md)
+ [

# Helper methods for CloudFront SaaS Manager properties
](saas-specific-logic-function-code.md)
+ [

# Use async and await
](async-await-syntax.md)
+ [

# CWT support for CloudFront Functions
](cwt-support-cloudfront-functions.md)
+ [

# General helper methods
](general-helper-methods.md)

# Determine function purpose
<a name="function-code-choose-purpose"></a>

Before you write your function code, determine the purpose of your function. Most functions in CloudFront Functions have one of the following purposes.

**Topics**
+ [

## Modify the HTTP request in a viewer request event type
](#function-code-modify-request)
+ [

## Generate an HTTP response in a viewer request event type
](#function-code-generate-response)
+ [

## Modify the HTTP response in a viewer response event type
](#function-code-modify-response)
+ [

## Validate mTLS connections in a connection request event type
](#function-code-connection-request)
+ [

## Related information
](#related-information-cloudfront-functions-purpose)

Regardless of your function’s purpose, the `handler` is the entry point for any function. It takes a single argument called `event`, which is passed to the function by CloudFront. The `event` is a JSON object that contains a representation of the HTTP request (and the response, if your function modifies the HTTP response). 

## Modify the HTTP request in a viewer request event type
<a name="function-code-modify-request"></a>

Your function can modify the HTTP request that CloudFront receives from the viewer (client), and return the modified request to CloudFront for continued processing. For example, your function code might normalize the [cache key](understanding-the-cache-key.md) or modify request headers.

After you create and publish a function that modifies the HTTP request, make sure to add an association for the *viewer request* event type. For more information, see [Create the function](functions-tutorial.md#functions-tutorial-create). This makes the function run each time that CloudFront receives a request from a viewer, before checking to see whether the requested object is in the CloudFront cache.

**Example**  
The following pseudocode shows the structure of a function that modifies the HTTP request.  

```
function handler(event) {
    var request = event.request;

    // Modify the request object here.

    return request;
}
```
The function returns the modified `request` object to CloudFront. CloudFront continues processing the returned request by checking the CloudFront cache for a cache hit, and sending the request to the origin if necessary.

## Generate an HTTP response in a viewer request event type
<a name="function-code-generate-response"></a>

Your function can generate an HTTP response at the edge and return it directly to the viewer (client) without checking for a cached response or any further processing by CloudFront. For example, your function code might redirect the request to a new URL, or check for authorization and return a `401` or `403` response to unauthorized requests.

When you create a function that generates an HTTP response, make sure to choose the *viewer request* event type. This means that the function runs each time CloudFront receives a request from a viewer, before CloudFront does any further processing of the request.

**Example**  
The following pseudocode shows the structure of a function that generates an HTTP response.  

```
function handler(event) {
    var request = event.request;

    var response = ...; // Create the response object here,
                        // using the request properties if needed.

    return response;
}
```
The function returns a `response` object to CloudFront, which CloudFront immediately returns to the viewer without checking the CloudFront cache or sending a request to the origin.

## Modify the HTTP response in a viewer response event type
<a name="function-code-modify-response"></a>

Your function can modify the HTTP response before CloudFront sends it to the viewer (client), regardless of whether the response comes from the CloudFront cache or the origin. For example, your function code might add or modify response headers, status codes, and body content.

When you create a function that modifies the HTTP response, make sure to choose the *viewer response* event type. This means that the function runs before CloudFront returns a response to the viewer, regardless of whether the response comes from the CloudFront cache or the origin.

**Example**  
The following pseudocode shows the structure of a function that modifies the HTTP response.  

```
function handler(event) {
    var request = event.request;
    var response = event.response;

    // Modify the response object here,
    // using the request properties if needed.

    return response;
}
```
The function returns the modified `response` object to CloudFront, which CloudFront immediately returns to the viewer.

## Validate mTLS connections in a connection request event type
<a name="function-code-connection-request"></a>

Connection functions are a type of CloudFront Functions that run during TLS connections to provide custom validation and authentication logic. Connection functions are currently available for mutual TLS (mTLS) connections, where you can validate client certificates and implement custom authentication logic beyond standard certificate validation. Connection functions run during the TLS handshake process and can allow or deny connections based on certificate properties, client IP addresses, or other criteria.

After you create and publish a connection function, make sure to add an association for the *connection request* event type with an mTLS-enabled distribution. This makes the function run each time a client attempts to establish an mTLS connection with CloudFront.

**Example**  
The following pseudocode shows the structure of a connection function:  

```
function connectionHandler(connection) {
    // Validate certificate and connection properties here.
    
    if (/* validation passes */) {
        connection.allow();
    } else {
        connection.deny();
    }
}
```
The function uses helper methods to determine whether to allow or deny the connection. Unlike viewer request and viewer response functions, connection functions cannot modify HTTP requests or responses.

## Related information
<a name="related-information-cloudfront-functions-purpose"></a>

For more information about working with CloudFront Functions, see the following topics:
+ [Event structure](functions-event-structure.md)
+ [JavaScript runtime features](functions-javascript-runtime-features.md)
+ [CloudFront Functions examples ](service_code_examples_cloudfront_functions_examples.md)
+ [Restrictions on edge functions](edge-functions-restrictions.md)

# CloudFront Functions event structure
<a name="functions-event-structure"></a>

CloudFront Functions passes an `event` object to your function code as input when it runs the function. When you [test a function](test-function.md), you create the `event` object and pass it to your function. When you create an `event` object for testing a function, you can omit the `distributionDomainName`, `distributionId`, and `requestId` fields in the `context` object. Make sure that the names of headers are lowercase, which is always the case in the `event` object that CloudFront Functions passes to your function in production.

The following shows an overview of the structure of this event object. 

```
{
    "version": "1.0",
    "context": {
        <context object>
    },
    "viewer": {
        <viewer object>
    },
    "request": {
        <request object>
    },
    "response": {
        <response object>
    }
}
```

For more information, see the following topics:

**Topics**
+ [

## Version field
](#functions-event-structure-version)
+ [

## Context object
](#functions-event-structure-context)
+ [

## Connection event structure
](#functions-event-structure-connection)
+ [

## Viewer object
](#functions-event-structure-viewer)
+ [

## Request object
](#functions-event-structure-request)
+ [

## Response object
](#functions-event-structure-response)
+ [

## Status code and body
](#functions-event-structure-status-body)
+ [

## Structure for a query string, header, or cookie
](#functions-event-structure-query-header-cookie)
+ [

## Example response object
](#functions-response-structure-example)
+ [

## Example event object
](#functions-event-structure-example)

## Version field
<a name="functions-event-structure-version"></a>

The `version` field contains a string that specifies the version of the CloudFront Functions event object. The current version is `1.0`.

## Context object
<a name="functions-event-structure-context"></a>

The `context` object contains contextual information about the event. It includes the following fields:

**`distributionDomainName`**  
The CloudFront domain name (for example, d111111abcdef8.cloudfront.net) of the standard distribution that's associated with the event.  
The `distributionDomainName` field only appears when your function is invoked for standard distributions.

**`endpoint`**  
The CloudFront domain name (for example, d111111abcdef8.cloudfront.net) of the connection group that's associated with the event.  
The `endpoint` field only appears when your function is invoked for multi-tenant distributions.

**`distributionId`**  
The ID of the distribution (for example, EDFDVBD6EXAMPLE) that's associated with the event.

**`eventType`**  
The event type, either `viewer-request` or `viewer-response`.

**`requestId`**  
A string that uniquely identifies a CloudFront request (and its associated response).

## Connection event structure
<a name="functions-event-structure-connection"></a>

Connection functions receive a different event structure than viewer functions. For detailed information about the connection event structure and response format, see [Associate a CloudFront Connection Function](connection-functions.md).

## Viewer object
<a name="functions-event-structure-viewer"></a>

The `viewer` object contains an `ip` field whose value is the IP address of the viewer (client) that sent the request. If the viewer request came through an HTTP proxy or a load balancer, the value is the IP address of the proxy or load balancer.

## Request object
<a name="functions-event-structure-request"></a>

The `request` object contains a representation of a viewer-to-CloudFront HTTP request. In the `event` object that's passed to your function, the `request` object represents the actual request that CloudFront received from the viewer.

If your function code returns a `request` object to CloudFront, it must use this same structure.

The `request` object contains the following fields:

**`method`**  
The HTTP method of the request. If your function code returns a `request`, it can't modify this field. This is the only read-only field in the `request` object.

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

**`querystring`**  
An object that represents the query string in the request. If the request doesn't include a query string, the `request` object still includes an empty `querystring` object.  
The `querystring` object contains one field for each query string parameter in the request.

**`headers`**  
An object that represents the HTTP headers in the request. If the request contains any `Cookie` headers, those headers are not part of the `headers` object. Cookies are represented separately in the `cookies` object.  
The `headers` object contains one field for each header in the request. Header names are converted to ASCII-lowercase in the event object, and header names must be ASCII-lowercase when they're added by your function code. When CloudFront Functions converts the event object back into an HTTP request, the first letter of each word in header names is capitalized, if it's an ASCII-letter. CloudFront Functions doesn't apply any changes to non-ASCII symbols in header names. For example, `TÈst-header` will become `tÈst-header` inside the function. The non-ASCII symbol `È` is unchanged.  
Words are separated by a hyphen (`-`). For example, if your function code adds a header named `example-header-name`, CloudFront converts this to `Example-Header-Name` in the HTTP request.

**`cookies`**  
An object that represents the cookies in the request (`Cookie` headers).  
The `cookies` object contains one field for each cookie in the request.

For more information about the structure of query strings, headers, and cookies, see [Structure for a query string, header, or cookie](#functions-event-structure-query-header-cookie).

For an example `event` object, see [Example event object](#functions-event-structure-example).

## Response object
<a name="functions-event-structure-response"></a>

The `response` object contains a representation of a CloudFront-to-viewer HTTP response. In the `event` object that's passed to your function, the `response` object represents CloudFront's actual response to a viewer request.

If your function code returns a `response` object, it must use this same structure.

The `response` object contains the following fields:

**`statusCode`**  
The HTTP status code of the response. This value is an integer, not a string.  
Your function can generate or modify the `statusCode`.

**`statusDescription`**  
The HTTP status description of the response. If your function code generates a response, this field is optional.

**`headers`**  
An object that represents the HTTP headers in the response. If the response contains any `Set-Cookie` headers, those headers are not part of the `headers` object. Cookies are represented separately in the `cookies` object.  
The `headers` object contains one field for each header in the response. Header names are converted to lowercase in the event object, and header names must be lowercase when they're added by your function code. When CloudFront Functions converts the event object back into an HTTP response, the first letter of each word in header names is capitalized. Words are separated by a hyphen (`-`). For example, if your function code adds a header named `example-header-name`, CloudFront converts this to `Example-Header-Name` in the HTTP response.

**`cookies`**  
An object that represents the cookies in the response (`Set-Cookie` headers).  
The `cookies` object contains one field for each cookie in the response.

**`body`**  
Adding the `body` field is optional, and it will not be present in the `response` object unless you specify it in your function. Your function does not have access to the original body returned by the CloudFront cache or origin. If you don't specify the `body` field in your viewer response function, the original body returned by the CloudFront cache or origin is returned to viewer.  
If you want CloudFront to return a custom body to the viewer, specify the body content in the `data` field, and the body encoding in the `encoding` field. You can specify the encoding as plain text (`"encoding": "text"`) or as Base64-encoded content (`"encoding": "base64"`).  
As a shortcut, you can also specify the body content directly in the `body` field (`"body": "<specify the body content here>"`). When you do this, omit the `data` and `encoding` fields. CloudFront treats the body as plain text in this case.    
`encoding`  
The encoding for the `body` content (`data` field). The only valid encodings are `text` and `base64`.  
If you specify `encoding` as `base64` but the body is not valid base64, CloudFront returns an error.  
`data`  
The `body` content.

For more information about modified status codes and body content, see [Status code and body](#functions-event-structure-status-body).

For more information about the structure of headers and cookies, see [Structure for a query string, header, or cookie](#functions-event-structure-query-header-cookie).

For an example `response` object, see [Example response object](#functions-response-structure-example).

## Status code and body
<a name="functions-event-structure-status-body"></a>

With CloudFront Functions, you can update the viewer response status code, replace the entire response body with a new one, or remove the response body. Some common scenarios for updating the viewer response after evaluating aspects of the response from the CloudFront cache or origin include the following:
+ Changing the status to set an HTTP 200 status code and creating static body content to return to the viewer.
+ Changing the status to set an HTTP 301 or 302 status code to redirect the user to another website.
+ Deciding whether to serve or drop the body of the viewer response.

**Note**  
If the origin returns an HTTP error of 400 and above, the CloudFront Function will not run. For more information see [Restrictions on all edge functions](edge-function-restrictions-all.md).

When you're working with the HTTP response, CloudFront Functions does not have access to the response body. You can replace the body content by setting it to the desired value, or you can remove the body by setting the value to be empty. If you don't update the body field in your function, the original body returned by the CloudFront cache or origin is returned back to viewer.

**Tip**  
When using CloudFront Functions to replace a body, be sure to align the corresponding headers, such as `content-encoding`, `content-type`, or `content-length`, to the new body content.   
For example, if the CloudFront origin or cache returns `content-encoding: gzip` but the viewer response function sets a body that's plain text, the function also needs to change the `content-encoding` and `content-type` headers accordingly.

If your CloudFront Function is configured to return an HTTP error of 400 or above, your viewer will not see a [custom error page](creating-custom-error-pages.md) that you have specified for the same status code.

## Structure for a query string, header, or cookie
<a name="functions-event-structure-query-header-cookie"></a>

Query strings, headers, and cookies share the same structure. Query strings can appear in requests. Headers appear in requests and responses. Cookies appear in requests and responses.

Each query string, header, or cookie is a unique field within the parent `querystring`, `headers`, or `cookies` object. The field name is the name of the query string, header, or cookie. Each field contains a `value` property with the value of the query string, header, or cookie.

**Contents**
+ [

### Query strings values or query string objects
](#functions-event-structure-query)
+ [

### Special considerations for headers
](#functions-event-structure-headers)
+ [

### Duplicate query strings, headers, and cookies (`multiValue` array)
](#functions-event-structure-multivalue)
+ [

### Cookie attributes
](#functions-event-structure-cookie-attributes)

### Query strings values or query string objects
<a name="functions-event-structure-query"></a>

A function can return a query string value in addition to a query string object. The query string value can be used to arrange the query string parameters in any custom order. 

**Example**  
To modify a query string in your function code, use code like the following.  

```
var request = event.request; 
request.querystring = 'ID=42&Exp=1619740800&TTL=1440&NoValue=&querymv=val1&querymv=val2,val3';
```

### Special considerations for headers
<a name="functions-event-structure-headers"></a>

For headers only, the header names are converted to lowercase in the event object, and header names must be lowercase when they're added by your function code. When CloudFront Functions converts the event object back into an HTTP request or response, the first letter of each word in header names is capitalized. Words are separated by a hyphen (`-`). For example, if your function code adds a header named `example-header-name`, CloudFront converts this to `Example-Header-Name` in the HTTP request or response.

**Example**  
Consider the following `Host` header in an HTTP request.  

```
Host: video.example.com
```
This header is represented as follows in the `request` object:  

```
"headers": {
    "host": {
        "value": "video.example.com"
    }
}
```
To access the `Host` header in your function code, use code like the following:  

```
var request = event.request;
var host = request.headers.host.value;
```
To add or modify a header in your function code, use code like the following (this code adds a header named `X-Custom-Header` with the value `example value`):  

```
var request = event.request;
request.headers['x-custom-header'] = {value: 'example value'};
```

### Duplicate query strings, headers, and cookies (`multiValue` array)
<a name="functions-event-structure-multivalue"></a>

An HTTP request or response can contain more than one query string, header, or cookie with the same name. In this case, the duplicate query strings, headers, or cookies are collapsed into one field in the `request` or `response` object, but this field contains an extra property named `multiValue`. The `multiValue` property contains an array with the values of each of the duplicate query strings, headers, or cookies.

**Example**  
Consider an HTTP request with the following `Accept` headers.  

```
Accept: application/json
Accept: application/xml
Accept: text/html
```
These headers are represented as follows in the `request` object.  

```
"headers": {
    "accept": {
        "value": "application/json",
        "multiValue": [
            {
                "value": "application/json"
            },
            {
                "value": "application/xml"
            },
            {
                "value": "text/html"
            }
        ]
    }
}
```

**Note**  
The first header value (in this case, `application/json`) is repeated in both the `value` and `multiValue` properties. This allows you to access *all* the values by looping through the `multiValue` array.

If your function code modifies a query string, header, or cookie that has a `multiValue` array, CloudFront Functions uses the following rules to apply the changes:

1. If the `multiValue` array exists and has any modification, then that modification is applied. The first element in the `value` property is ignored.

1. Otherwise, any modification to the `value` property is applied, and subsequent values (if they exist) remain unchanged.

The `multiValue` property is used only when the HTTP request or response contains duplicate query strings, headers, or cookies with the same name, as shown in the preceding example. However, if there are multiple values in a single query string, header, or cookie, the `multiValue` property is not used.

**Example**  
Consider a request with one `Accept` header that contains three values.  

```
Accept: application/json, application/xml, text/html
```
This header is represented as follows in the `request` object.  

```
"headers": {
    "accept": {
        "value": "application/json, application/xml, text/html"
    }
}
```

### Cookie attributes
<a name="functions-event-structure-cookie-attributes"></a>

In a `Set-Cookie` header in an HTTP response, the header contains the name–value pair for the cookie and optionally a set of attributes separated by semicolons. 

**Example**  

```
Set-Cookie: cookie1=val1; Secure; Path=/; Domain=example.com; Expires=Wed, 05 Apr 2021 07:28:00 GMT
```
In the `response` object, these attributes are represented in the `attributes` property of the cookie field. For example, the preceding `Set-Cookie` header is represented as follows:  

```
"cookie1": {
    "value": "val1",
    "attributes": "Secure; Path=/; Domain=example.com; Expires=Wed, 05 Apr 2021 07:28:00 GMT"
}
```

## Example response object
<a name="functions-response-structure-example"></a>

The following example shows a `response` object — the output of a viewer response function — in which the body has been replaced by a viewer response function.

```
{
  "response": {
    "statusCode": 200,
    "statusDescription": "OK",
    "headers": {
      "date": {
        "value": "Mon, 04 Apr 2021 18:57:56 GMT"
      },
      "server": {
        "value": "gunicorn/19.9.0"
      },
      "access-control-allow-origin": {
        "value": "*"
      },
      "access-control-allow-credentials": {
        "value": "true"
      },
      "content-type": {
        "value": "text/html"
      },
      "content-length": {
        "value": "86"
      }
    },
    "cookies": {
      "ID": {
        "value": "id1234",
        "attributes": "Expires=Wed, 05 Apr 2021 07:28:00 GMT"
      },
      "Cookie1": {
        "value": "val1",
        "attributes": "Secure; Path=/; Domain=example.com; Expires=Wed, 05 Apr 2021 07:28:00 GMT",
        "multiValue": [
          {
            "value": "val1",
            "attributes": "Secure; Path=/; Domain=example.com; Expires=Wed, 05 Apr 2021 07:28:00 GMT"
          },
          {
            "value": "val2",
            "attributes": "Path=/cat; Domain=example.com; Expires=Wed, 10 Jan 2021 07:28:00 GMT"
          }
        ]
      }
    },
    
    // Adding the body field is optional and it will not be present in the response object
    // unless you specify it in your function.
    // Your function does not have access to the original body returned by the CloudFront
    // cache or origin.
    // If you don't specify the body field in your viewer response function, the original
    // body returned by the CloudFront cache or origin is returned to viewer.

     "body": {
      "encoding": "text",
      "data": "<!DOCTYPE html><html><body><p>Here is your custom content.</p></body></html>"
    }
  }
}
```

## Example event object
<a name="functions-event-structure-example"></a>

The following example shows a complete `event` object. This is an example invocation for a standard distribution, and not for a multi-tenant distribution. For multi-tenant distributions, the `endpoint` field is used instead of `distributionDomainName` The value of `endpoint` is the CloudFront domain name (for example, d111111abcdef8.cloudfront.net) of the connection group that’s associated with the event.

**Note**  
The `event` object is the input to your function. Your function returns only the `request` or `response` object, not the complete `event` object.

```
{
    "version": "1.0",
    "context": {
        "distributionDomainName": "d111111abcdef8.cloudfront.net",
        "distributionId": "EDFDVBD6EXAMPLE",
        "eventType": "viewer-response",
        "requestId": "EXAMPLEntjQpEXAMPLE_SG5Z-EXAMPLEPmPfEXAMPLEu3EqEXAMPLE=="
    },
    "viewer": {"ip": "198.51.100.11"},
    "request": {
        "method": "GET",
        "uri": "/media/index.mpd",
        "querystring": {
            "ID": {"value": "42"},
            "Exp": {"value": "1619740800"},
            "TTL": {"value": "1440"},
            "NoValue": {"value": ""},
            "querymv": {
                "value": "val1",
                "multiValue": [
                    {"value": "val1"},
                    {"value": "val2,val3"}
                ]
            }
        },
        "headers": {
            "host": {"value": "video.example.com"},
            "user-agent": {"value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0"},
            "accept": {
                "value": "application/json",
                "multiValue": [
                    {"value": "application/json"},
                    {"value": "application/xml"},
                    {"value": "text/html"}
                ]
            },
            "accept-language": {"value": "en-GB,en;q=0.5"},
            "accept-encoding": {"value": "gzip, deflate, br"},
            "origin": {"value": "https://website.example.com"},
            "referer": {"value": "https://website.example.com/videos/12345678?action=play"},
            "cloudfront-viewer-country": {"value": "GB"}
        },
        "cookies": {
            "Cookie1": {"value": "value1"},
            "Cookie2": {"value": "value2"},
            "cookie_consent": {"value": "true"},
            "cookiemv": {
                "value": "value3",
                "multiValue": [
                    {"value": "value3"},
                    {"value": "value4"}
                ]
            }
        }
    },
    "response": {
        "statusCode": 200,
        "statusDescription": "OK",
        "headers": {
            "date": {"value": "Mon, 04 Apr 2021 18:57:56 GMT"},
            "server": {"value": "gunicorn/19.9.0"},
            "access-control-allow-origin": {"value": "*"},
            "access-control-allow-credentials": {"value": "true"},
            "content-type": {"value": "application/json"},
            "content-length": {"value": "701"}
        },
        "cookies": {
            "ID": {
                "value": "id1234",
                "attributes": "Expires=Wed, 05 Apr 2021 07:28:00 GMT"
            },
            "Cookie1": {
                "value": "val1",
                "attributes": "Secure; Path=/; Domain=example.com; Expires=Wed, 05 Apr 2021 07:28:00 GMT",
                "multiValue": [
                    {
                        "value": "val1",
                        "attributes": "Secure; Path=/; Domain=example.com; Expires=Wed, 05 Apr 2021 07:28:00 GMT"
                    },
                    {
                        "value": "val2",
                        "attributes": "Path=/cat; Domain=example.com; Expires=Wed, 10 Jan 2021 07:28:00 GMT"
                    }
                ]
            }
        }
    }
}
```

# JavaScript runtime features for CloudFront Functions
<a name="functions-javascript-runtime-features"></a>

The CloudFront Functions JavaScript runtime environment is compliant with [ECMAScript (ES) version 5.1](https://www.ecma-international.org/ecma-262/5.1/) and also supports some features of ES versions 6 through 12.

For the most up-to-date features, we recommend that you use JavaScript runtime 2.0. 

The JavaScript runtime 2.0 features has the following changes compared to 1.0:
+ Buffer module methods are available
+ The following non-standard string prototype methods are not available:
  + `String.prototype.bytesFrom()`
  + `String.prototype.fromBytes()`
  + `String.prototype.fromUTF8()`
  + `String.prototype.toBytes()`
  + `String.prototype.toUTF8()`
+ The cryptographic module has the following changes:
  + `hash.digest()` – Return type is changed to `Buffer` if no encoding is provided
  + `hmac.digest()` – Return type is changed to `Buffer` if no encoding is provided
+ For more information about additional new features, see [JavaScript runtime 2.0 features for CloudFront Functions](functions-javascript-runtime-20.md).

**Topics**
+ [JavaScript runtime 1.0 features](functions-javascript-runtime-10.md)
+ [JavaScript runtime 2.0 features](functions-javascript-runtime-20.md)

# JavaScript runtime 1.0 features for CloudFront Functions
<a name="functions-javascript-runtime-10"></a>

The CloudFront Functions JavaScript runtime environment is compliant with [ECMAScript (ES) version 5.1](https://262.ecma-international.org/5.1/) and also supports some features of ES versions 6 through 9. It also provides some nonstandard methods that are not part of the ES specifications. 

The following topics list all the supported language features.

**Topics**
+ [

## Core features
](#writing-functions-javascript-features-core)
+ [

## Primitive objects
](#writing-functions-javascript-features-primitive-objects)
+ [

## Built-in objects
](#writing-functions-javascript-features-builtin-objects)
+ [

## Error types
](#writing-functions-javascript-features-error-types)
+ [

## Globals
](#writing-functions-javascript-features-globals)
+ [

## Built-in modules
](#writing-functions-javascript-features-builtin-modules)
+ [

## Restricted features
](#writing-functions-javascript-features-restricted-features)

## Core features
<a name="writing-functions-javascript-features-core"></a>

The following core features of ES are supported.

**Types**  
All ES 5.1 types are supported. This includes Boolean values, numbers, strings, objects, arrays, functions, function constructors, and regular expressions.

**Operators**  
All ES 5.1 operators are supported.  
The ES 7 exponentiation operator (`**`) is supported.

**Statements**  
The `const` and `let` statements are not supported.
The following ES 5.1 statements are supported:  
+ `break`
+ `catch`
+ `continue`
+ `do-while`
+ `else`
+ `finally`
+ `for`
+ `for-in`
+ `if`
+ `return`
+ `switch`
+ `throw`
+ `try`
+ `var`
+ `while`
+ Labeled statements

**Literals**  
ES 6 template literals are supported: multiline strings, expression interpolation, and nesting templates.

**Functions**  
All ES 5.1 function features are supported.  
ES 6 arrow functions are supported, and ES 6 rest parameter syntax is supported.

**Unicode**  
Source text and string literals can contain Unicode-encoded characters. Unicode code point escape sequences of six characters (for example, `\uXXXX`) are also supported.

**Strict mode**  
Functions operate in strict mode by default, so you don’t need to add a `use strict` statement in your function code. This cannot be changed.

## Primitive objects
<a name="writing-functions-javascript-features-primitive-objects"></a>

The following primitive objects of ES are supported.

**Object**  
The following ES 5.1 methods on objects are supported:  
+ `create` (without properties list)
+ `defineProperties`
+ `defineProperty`
+ `freeze`
+ `getOwnPropertyDescriptor`
+ `getOwnPropertyNames`
+ `getPrototypeOf`
+ `hasOwnProperty`
+ `isExtensible`
+ `isFrozen`
+ `prototype.isPrototypeOf`
+ `isSealed`
+ `keys`
+ `preventExtensions`
+ `prototype.propertyIsEnumerable`
+ `seal`
+ `prototype.toString`
+ `prototype.valueOf`
The following ES 6 methods on objects are supported:  
+ `assign`
+ `is`
+ `prototype.setPrototypeOf`
The following ES 8 methods on objects are supported:  
+ `entries`
+ `values`

**String**  
The following ES 5.1 methods on strings are supported:  
+ `fromCharCode`
+ `prototype.charAt`
+ `prototype.concat`
+ `prototype.indexOf`
+ `prototype.lastIndexOf`
+ `prototype.match`
+ `prototype.replace`
+ `prototype.search`
+ `prototype.slice`
+ `prototype.split`
+ `prototype.substr`
+ `prototype.substring`
+ `prototype.toLowerCase`
+ `prototype.trim`
+ `prototype.toUpperCase`
The following ES 6 methods on strings are supported:  
+ `fromCodePoint`
+ `prototype.codePointAt`
+ `prototype.endsWith`
+ `prototype.includes`
+ `prototype.repeat`
+ `prototype.startsWith`
The following ES 8 methods on strings are supported:  
+ `prototype.padStart`
+ `prototype.padEnd`
The following ES 9 methods on strings are supported:  
+ `prototype.trimStart`
+ `prototype.trimEnd`
The following nonstandard methods on strings are supported:  
+ `prototype.bytesFrom(array | string, encoding)`

  Creates a byte string from an array of octets or an encoded string. The string encoding options are `hex`, `base64`, and `base64url`.
+ `prototype.fromBytes(start[, end])`

  Creates a Unicode string from a byte string where each byte is replaced with the corresponding Unicode code point.
+ `prototype.fromUTF8(start[, end])`

  Creates a Unicode string from a UTF-8 encoded byte string. If the encoding is incorrect, it returns `null`.
+ `prototype.toBytes(start[, end])`

  Creates a byte string from a Unicode string. All characters must be in the [0,255] range. If not, it returns `null`.
+ `prototype.toUTF8(start[, end])`

  Creates a UTF-8 encoded byte string from a Unicode string.

**Number**  
All ES 5.1 methods on numbers are supported.  
The following ES 6 methods on numbers are supported:  
+ `isFinite`
+ `isInteger`
+ `isNaN`
+ `isSafeInteger`
+ `parseFloat`
+ `parseInt`
+ `prototype.toExponential`
+ `prototype.toFixed`
+ `prototype.toPrecision`
+ `EPSILON`
+ `MAX_SAFE_INTEGER`
+ `MAX_VALUE`
+ `MIN_SAFE_INTEGER`
+ `MIN_VALUE`
+ `NEGATIVE_INFINITY`
+ `NaN`
+ `POSITIVE_INFINITY`

## Built-in objects
<a name="writing-functions-javascript-features-builtin-objects"></a>

The following built-in objects of ES are supported.

**Math**  
All ES 5.1 math methods are supported.  
In the CloudFront Functions runtime environment, the `Math.random()` implementation uses OpenBSD `arc4random` seeded with the timestamp of when the function runs.
The following ES 6 math methods are supported:  
+ `acosh`
+ `asinh`
+ `atanh`
+ `cbrt`
+ `clz32`
+ `cosh`
+ `expm1`
+ `fround`
+ `hypot`
+ `imul`
+ `log10`
+ `log1p`
+ `log2`
+ `sign`
+ `sinh`
+ `tanh`
+ `trunc`
+ `E`
+ `LN10`
+ `LN2`
+ `LOG10E`
+ `LOG2E`
+ `PI`
+ `SQRT1_2`
+ `SQRT2`

**Date**  
All ES 5.1 `Date` features are supported.  
For security reasons, `Date` always returns the same value—the function’s start time—during the lifetime of a single function run. For more information, see [Restricted features](#writing-functions-javascript-features-restricted-features).

**Function**  
The `apply`, `bind`, and `call` methods are supported.  
Function constructors are not supported.

**Regular expressions**  
All ES 5.1 regular expression features are supported. The regular expression language is Perl compatible. ES 9 named capture groups are supported.

**JSON**  
All ES 5.1 JSON features are supported, including `parse` and `stringify`.

**Array**  
The following ES 5.1 methods on arrays are supported:  
+ `isArray`
+ `prototype.concat`
+ `prototype.every`
+ `prototype.filter`
+ `prototype.forEach`
+ `prototype.indexOf`
+ `prototype.join`
+ `prototype.lastIndexOf`
+ `prototype.map`
+ `prototype.pop`
+ `prototype.push`
+ `prototype.reduce`
+ `prototype.reduceRight`
+ `prototype.reverse`
+ `prototype.shift`
+ `prototype.slice`
+ `prototype.some`
+ `prototype.sort`
+ `prototype.splice`
+ `prototype.unshift`
The following ES 6 methods on arrays are supported:  
+ `of`
+ `prototype.copyWithin`
+ `prototype.fill`
+ `prototype.find`
+ `prototype.findIndex`
The following ES 7 methods on arrays are supported:  
+ `prototype.includes`

**Typed arrays**  
The following ES 6 typed arrays are supported:  
+ `Int8Array`
+ `Uint8Array`
+ `Uint8ClampedArray`
+ `Int16Array`
+ `Uint16Array`
+ `Int32Array`
+ `Uint32Array`
+ `Float32Array`
+ `Float64Array`
+ `prototype.copyWithin`
+ `prototype.fill`
+ `prototype.join`
+ `prototype.set`
+ `prototype.slice`
+ `prototype.subarray`
+ `prototype.toString`

**ArrayBuffer**  
The following methods on `ArrayBuffer` are supported:  
+ `prototype.isView`
+ `prototype.slice`

**Promise**  
The following methods on promises are supported:  
+ `reject`
+ `resolve`
+ `prototype.catch`
+ `prototype.finally`
+ `prototype.then`

**Crypto**  
The cryptographic module provides standard hashing and hash-based message authentication code (HMAC) helpers. You can load the module using `require('crypto')`. The module exposes the following methods that behave exactly as their Node.js counterparts:  
+ `createHash(algorithm)`
+ `hash.update(data)`
+ `hash.digest([encoding])`
+ `createHmac(algorithm, secret key)`
+ `hmac.update(data)`
+ `hmac.digest([encoding])`
For more information, see [Crypto (hash and HMAC)](#writing-functions-javascript-features-builtin-modules-crypto) in the built-in modules section.

**Console**  
This is a helper object for debugging. It only supports the `log()` method, to record log messages.  
CloudFront Functions doesn't support comma syntax, such as `console.log('a', 'b')`. Instead, use the `console.log('a' + ' ' + 'b')` format.

## Error types
<a name="writing-functions-javascript-features-error-types"></a>

The following error objects are supported:
+ `Error`
+ `EvalError`
+ `InternalError`
+ `MemoryError`
+ `RangeError`
+ `ReferenceError`
+ `SyntaxError`
+ `TypeError`
+ `URIError`

## Globals
<a name="writing-functions-javascript-features-globals"></a>

The `globalThis` object is supported.

The following ES 5.1 global functions are supported:
+ `decodeURI`
+ `decodeURIComponent`
+ `encodeURI`
+ `encodeURIComponent`
+ `isFinite`
+ `isNaN`
+ `parseFloat`
+ `parseInt`

The following global constants are supported:
+ `NaN`
+ `Infinity`
+ `undefined`

## Built-in modules
<a name="writing-functions-javascript-features-builtin-modules"></a>

The following built-in modules are supported.

**Topics**
+ [

### Crypto (hash and HMAC)
](#writing-functions-javascript-features-builtin-modules-crypto)
+ [

### Query string
](#writing-functions-javascript-features-builtin-modules-query-string)

### Crypto (hash and HMAC)
<a name="writing-functions-javascript-features-builtin-modules-crypto"></a>

The cryptographic module (`crypto`) provides standard hashing and hash-based message authentication code (HMAC) helpers. You can load the module using `require('crypto')`. The module provides the following methods that behave exactly as their Node.js counterparts.

**Hashing methods**

`crypto.createHash(algorithm)`  
Creates and returns a hash object that you can use to generate hash digests using the given algorithm: `md5`, `sha1`, or `sha256`.

`hash.update(data)`  
Updates the hash content with the given `data`.

`hash.digest([encoding])`  
Calculates the digest of all of the data passed using `hash.update()`. The encoding can be `hex`, `base64`, or `base64url`.

**HMAC methods**

`crypto.createHmac(algorithm, secret key)`  
Creates and returns an HMAC object that uses the given `algorithm` and `secret key`. The algorithm can be `md5`, `sha1`, or `sha256`.

`hmac.update(data)`  
Updates the HMAC content with the given `data`.

`hmac.digest([encoding])`  
Calculates the digest of all of the data passed using `hmac.update()`. The encoding can be `hex`, `base64`, or `base64url`.

### Query string
<a name="writing-functions-javascript-features-builtin-modules-query-string"></a>

**Note**  
The [CloudFront Functions event object](functions-event-structure.md) automatically parses URL query strings for you. That means that in most cases you don’t need to use this module.

The query string module (`querystring`) provides methods for parsing and formatting URL query strings. You can load the module using `require('querystring')`. The module provides the following methods.

`querystring.escape(string)`  
URL-encodes the given `string`, returning an escaped query string. The method is used by `querystring.stringify()` and should not be used directly.

`querystring.parse(string[, separator[, equal[, options]]])`  
Parses a query string (`string`) and returns an object.  
The `separator` parameter is a substring for delimiting key and value pairs in the query string. By default it is `&`.  
The `equal` parameter is a substring for delimiting keys and values in the query string. By default it is `=`.  
The `options` parameter is an object with the following keys:    
`decodeURIComponent function`  
A function to decode percent-encoded characters in the query string. By default it is `querystring.unescape()`.  
`maxKeys number`  
The maximum number of keys to parse. By default it is `1000`. Use a value of `0` to remove the limitations for counting keys.
By default, percent-encoded characters within the query string are assumed to use the UTF-8 encoding. Invalid UTF-8 sequences are replaced with the `U+FFFD` replacement character.  
For example, for the following query string:  

```
'name=value&abc=xyz&abc=123'
```
The return value of `querystring.parse()` is:  

```
{
name: 'value',
abc: ['xyz', '123']
}
```
`querystring.decode()` is an alias for `querystring.parse()`.

`querystring.stringify(object[, separator[, equal[, options]]])`  
Serializes an `object` and returns a query string.  
The `separator` parameter is a substring for delimiting key and value pairs in the query string. By default it is `&`.  
The `equal` parameter is a substring for delimiting keys and values in the query string. By default it is `=`.  
The `options` parameter is an object with the following keys:    
`encodeURIComponent function`  
The function to use for converting URL-unsafe characters to percent-encoding in the query string. By default it is `querystring.escape()`.
By default, characters that require percent-encoding within the query string are encoded as UTF-8. To use a different encoding, specify the `encodeURIComponent` option.  
For example, for the following code:  

```
querystring.stringify({ name: 'value', abc: ['xyz', '123'], anotherName: '' });
```
The return value is:  

```
'name=value&abc=xyz&abc=123&anotherName='
```
`querystring.encode()` is an alias for `querystring.stringify()`.

`querystring.unescape(string)`  
Decodes URL percent-encoded characters in the given `string`, returning an unescaped query string. This method is used by `querystring.parse()` and should not be used directly.

## Restricted features
<a name="writing-functions-javascript-features-restricted-features"></a>

The following JavaScript language features are either unsupported or restricted due to security concerns.

**Dynamic code evaluation**  
Dynamic code evaluation is not supported. Both `eval()` and `Function` constructors throw an error if attempted. For example, `const sum = new Function('a', 'b', 'return a + b')` throws an error.

**Timers**  
The `setTimeout()`, `setImmediate()`, and `clearTimeout()` functions are not supported. There is no provision to defer or yield within a function run. Your function must synchronously run to completion.

**Date and timestamps**  
For security reasons, there is no access to high-resolution timers. All `Date` methods to query the current time always return the same value during the lifetime of a single function run. The returned timestamp is the time when the function started running. Consequently, you cannot measure elapsed time in your function.

**File system access**  
There is no file system access. For example, there is no `fs` module for file system access like there is in Node.js.

**Process access**  
There is no process access. For example, there is no `process` global object for processing information access like there is in Node.js.

**Environment variables**  
There is no access to environment variables.   
Instead, you can use CloudFront KeyValueStore to create a centralized datastore of key-value pairs for your CloudFront Functions. CloudFront KeyValueStore enables dynamic updates to your configuration data without needing to deploy code changes. You must use [JavaScript runtime 2.0](functions-javascript-runtime-20.md) to use CloudFront KeyValueStore. For more information, see [Amazon CloudFront KeyValueStore](kvs-with-functions.md).

**Network access**  
There is no support for network calls. For example, XHR, HTTP(S), and socket are not supported.

# JavaScript runtime 2.0 features for CloudFront Functions
<a name="functions-javascript-runtime-20"></a>

The CloudFront Functions JavaScript runtime environment is compliant with [ECMAScript (ES) version 5.1](https://262.ecma-international.org/5.1/) and also supports some features of ES versions 6 through 12. It also provides some nonstandard methods that are not part of the ES specifications. The following topics list all supported features in this runtime.

**Topics**
+ [

## Core features
](#writing-functions-javascript-features-core-20)
+ [

## Primitive objects
](#writing-functions-javascript-features-primitive-objects-20)
+ [

## Built-in objects
](#writing-functions-javascript-features-builtin-objects-20)
+ [

## Error types
](#writing-functions-javascript-features-error-types-20)
+ [

## Globals
](#writing-functions-javascript-features-globals-20)
+ [

## Built-in modules
](#writing-functions-javascript-features-builtin-modules-20)
+ [

## Restricted features
](#writing-functions-javascript-features-restricted-features-20)

## Core features
<a name="writing-functions-javascript-features-core-20"></a>

The following core features of ES are supported.

**Types**  
All ES 5.1 types are supported. This includes boolean values, numbers, strings, objects, arrays, functions, and regular expressions.

**Operators**  
All ES 5.1 operators are supported.  
The ES 7 exponentiation operator (`**`) is supported.

**Statements**  
The following ES 5.1 statements are supported:  
+ `break`
+ `catch`
+ `continue`
+ `do-while`
+ `else`
+ `finally`
+ `for`
+ `for-in`
+ `if`
+ `label`
+ `return`
+ `switch`
+ `throw`
+ `try`
+ `var`
+ `while`
The following ES 6 statements are supported:  
+ `const`
+ `let`
The following ES 8 statements are supported:  
+ `async`
+ `await`
`async`, `await`, `const`, and `let` are supported in JavaScript runtime 2.0.  
`await` can be used inside `async` functions only. `async` arguments and closures are not supported.

**Literals**  
ES 6 template literals are supported: multiline strings, expression interpolation, and nesting templates.

**Functions**  
All ES 5.1 function features are supported.  
ES 6 arrow functions are supported, and ES 6 rest parameter syntax is supported.

**Unicode**  
Source text and string literals can contain Unicode-encoded characters. Unicode code point escape sequences of six characters (for example, `\uXXXX`) are also supported.

**Strict mode**  
Functions operate in strict mode by default, so you don’t need to add a `use strict` statement in your function code. This cannot be changed.

## Primitive objects
<a name="writing-functions-javascript-features-primitive-objects-20"></a>

The following primitive objects of ES are supported.

**Object**  
The following ES 5.1 methods on objects are supported:  
+ `Object.create()` (without properties list)
+ `Object.defineProperties()`
+ `Object.defineProperty()`
+ `Object.freeze()`
+ `Object.getOwnPropertyDescriptor()`
+ `Object.getOwnPropertyDescriptors()`
+ `Object.getOwnPropertyNames()`
+ `Object.getPrototypeOf()`
+ `Object.isExtensible()`
+ `Object.isFrozen()`
+ `Object.isSealed()`
+ `Object.keys()`
+ `Object.preventExtensions()`
+ `Object.seal()`
The following ES 6 methods on objects are supported:  
+ `Object.assign()`
The following ES 8 methods on objects are supported:  
+ `Object.entries()`
+ `Object.values()`
The following ES 5.1 prototype methods on objects are supported:  
+ `Object.prototype.hasOwnProperty()`
+ `Object.prototype.isPrototypeOf()`
+ `Object.prototype.propertyIsEnumerable()`
+ `Object.prototype.toString()`
+ `Object.prototype.valueOf()`
The following ES 6 prototype methods on objects are supported:  
+ `Object.prototype.is()`
+ `Object.prototype.setPrototypeOf()`

**String**  
The following ES 5.1 methods on strings are supported:  
+ `String.fromCharCode()`
The following ES 6 methods on strings are supported:  
+ `String.fromCodePoint()`
The following ES 5.1 prototype methods on strings are supported:  
+ `String.prototype.charAt()`
+ `String.prototype.concat()`
+ `String.prototype.indexOf()`
+ `String.prototype.lastIndexOf()`
+ `String.prototype.match()`
+ `String.prototype.replace()`
+ `String.prototype.search()`
+ `String.prototype.slice()`
+ `String.prototype.split()`
+ `String.prototype.substr()`
+ `String.prototype.substring()`
+ `String.prototype.toLowerCase()`
+ `String.prototype.trim()`
+ `String.prototype.toUpperCase()`
The following ES 6 prototype methods on strings are supported:  
+ `String.prototype.codePointAt()`
+ `String.prototype.endsWith()`
+ `String.prototype.includes()`
+ `String.prototype.repeat()`
+ `String.prototype.startsWith()`
The following ES 8 prototype methods on strings are supported:  
+ `String.prototype.padStart()`
+ `String.prototype.padEnd()`
The following ES 9 prototype methods on strings are supported:  
+ `String.prototype.trimStart()`
+ `String.prototype.trimEnd()`
The following ES 12 prototype methods on strings are supported:  
+ `String.prototype.replaceAll()`
**Note**  
`String.prototype.replaceAll()` is new in JavaScript runtime 2.0.

**Number**  
ALL ES 5 numbers are supported.  
The following ES 6 properties on numbers are supported:  
+ `Number.EPSILON`
+ `Number.MAX_SAFE_INTEGER`
+ `Number.MIN_SAFE_INTEGER`
+ `Number.MAX_VALUE`
+ `Number.MIN_VALUE`
+ `Number.NaN`
+ `Number.NEGATIVE_INFINITY`
+ `Number.POSITIVE_INFINITY`
The following ES 6 methods on numbers are supported:  
+ `Number.isFinite()`
+ `Number.isInteger()`
+ `Number.isNaN()`
+ `Number.isSafeInteger()`
+ `Number.parseInt()`
+ `Number.parseFloat()`
The following ES 5.1 prototype methods on numbers are supported:  
+ `Number.prototype.toExponential()`
+ `Number.prototype.toFixed()`
+ `Number.prototype.toPrecision()`
ES 12 numeric separators are supported.  
ES 12 numeric separators are new in JavaScript runtime 2.0.

## Built-in objects
<a name="writing-functions-javascript-features-builtin-objects-20"></a>

The following built-in objects of ES are supported.

**Math**  
All ES 5.1 math methods are supported.  
In the CloudFront Functions runtime environment, the `Math.random()` implementation uses OpenBSD `arc4random` seeded with the timestamp of when the function runs.
The following ES 6 math properties are supported:  
+ `Math.E`
+ `Math.LN10`
+ `Math.LN2`
+ `Math.LOG10E`
+ `Math.LOG2E`
+ `Math.PI`
+ `Math.SQRT1_2`
+ `Math.SQRT2`
The following ES 6 math methods are supported:  
+ `Math.abs()`
+ `Math.acos()`
+ `Math.acosh()`
+ `Math.asin()`
+ `Math.asinh()`
+ `Math.atan()`
+ `Math.atan2()`
+ `Math.atanh()`
+ `Math.cbrt()`
+ `Math.ceil()`
+ `Math.clz32()`
+ `Math.cos()`
+ `Math.cosh()`
+ `Math.exp()`
+ `Math.expm1()`
+ `Math.floor()`
+ `Math.fround()`
+ `Math.hypot()`
+ `Math.imul()`
+ `Math.log()`
+ `Math.log1p()`
+ `Math.log2()`
+ `Math.log10()`
+ `Math.max()`
+ `Math.min()`
+ `Math.pow()`
+ `Math.random()`
+ `Math.round()`
+ `Math.sign()`
+ `Math.sinh()`
+ `Math.sin()`
+ `Math.sqrt()`
+ `Math.tan()`
+ `Math.tanh()`
+ `Math.trunc()`

**Date**  
All ES 5.1 `Date` features are supported.  
For security reasons, `Date` always returns the same value—the function’s start time—during the lifetime of a single function run. For more information, see [Restricted features](functions-javascript-runtime-10.md#writing-functions-javascript-features-restricted-features).

**Function**  
The following ES 5.1 prototype methods are supported:  
+ `Function.prototype.apply()`
+ `Function.prototype.bind()`
+ `Function.prototype.call()`
Function constructors are not supported.

**Regular expressions**  
All ES 5.1 regular expression features are supported. The regular expression language is Perl compatible.  
The following ES 5.1 prototype accessor properties are supported:  
+ `RegExp.prototype.global`
+ `RegExp.prototype.ignoreCase`
+ `RegExp.protoype.multiline`
+ `RegExp.protoype.source`
+ `RegExp.prototype.sticky`
+ `RegExp.prototype.flags`
**Note**  
`RegExp.prototype.sticky` and `RegExp.prototype.flags` are new in JavaScript runtime 2.0.
The following ES 5.1 prototype methods are supported:  
+ `RegExp.prototype.exec()`
+ `RegExp.prototype.test()`
+ `RegExp.prototype.toString()`
+ `RegExp.prototype[@@replace]()`
+ `RegExp.prototype[@@split]()`
**Note**  
`RegExp.prototype[@@split]()` is new in JavaScript runtime 2.0.
The following ES 5.1 instance properties are supported:  
+ `lastIndex`
ES 9 named capture groups are supported.

**JSON**  
The following ES 5.1 methods are supported:  
+ `JSON.parse()`
+ `JSON.stringify()`

**Array**  
The following ES 5.1 methods on arrays are supported:  
+ `Array.isArray()`
The following ES 6 methods on arrays are supported:  
+ `Array.of()`
The following ES 5.1 prototype methods are supported:  
+ `Array.prototype.concat()`
+ `Array.prototype.every()`
+ `Array.prototype.filter()`
+ `Array.prototype.forEach()`
+ `Array.prototype.indexOf()`
+ `Array.prototype.join()`
+ `Array.prototype.lastIndexOf()`
+ `Array.prototype.map()`
+ `Array.prototype.pop()`
+ `Array.prototype.push()`
+ `Array.prototype.reduce()`
+ `Array.prototype.reduceRight()`
+ `Array.prototype.reverse()`
+ `Array.prototype.shift()`
+ `Array.prototype.slice()`
+ `Array.prototype.some()`
+ `Array.prototype.sort()`
+ `Array.prototype.splice()`
+ `Array.prototype.unshift()`
The following ES 6 prototype methods are supported  
+ `Array.prototype.copyWithin()`
+ `Array.prototype.fill()`
+ `Array.prototype.find()`
+ `Array.prototype.findIndex()`
The following ES 7 prototype methods are supported:  
+ `Array.prototype.includes()`

**Typed arrays**  
The following ES 6 typed array constructors are supported:  
+ `Float32Array`
+ `Float64Array`
+ `Int8Array`
+ `Int16Array`
+ `Int32Array`
+ `Uint8Array`
+ `Uint8ClampedArray`
+ `Uint16Array`
+ `Uint32Array`
The following ES 6 methods are supported:  
+ `TypedArray.from()`
+ `TypedArray.of()`
**Note**  
`TypedArray.from()` and `TypedArray.of()` are new in JavaScript runtime 2.0.
The following ES 6 prototype methods are supported:  
+ `TypedArray.prototype.copyWithin()`
+ `TypedArray.prototype.every()`
+ `TypedArray.prototype.fill()`
+ `TypedArray.prototype.filter()`
+ `TypedArray.prototype.find()`
+ `TypedArray.prototype.findIndex()`
+ `TypedArray.prototype.forEach()`
+ `TypedArray.prototype.includes()`
+ `TypedArray.prototype.indexOf()`
+ `TypedArray.prototype.join()`
+ `TypedArray.prototype.lastIndexOf()`
+ `TypedArray.prototype.map()`
+ `TypedArray.prototype.reduce()`
+ `TypedArray.prototype.reduceRight()`
+ `TypedArray.prototype.reverse()`
+ `TypedArray.prototype.some()`
+ `TypedArray.prototype.set()`
+ `TypedArray.prototype.slice()`
+ `TypedArray.prototype.sort()`
+ `TypedArray.prototype.subarray()`
+ `TypedArray.prototype.toString()`
**Note**  
`TypedArray.prototype.every()`, `TypedArray.prototype.fill()`, `TypedArray.prototype.filter()`, `TypedArray.prototype.find()`, `TypedArray.prototype.findIndex()`, `TypedArray.prototype.forEach()`, `TypedArray.prototype.includes()`, `TypedArray.prototype.indexOf()`, `TypedArray.prototype.join()`, `TypedArray.prototype.lastIndexOf()`,`TypedArray.prototype.map()`, `TypedArray.prototype.reduce()`, `TypedArray.prototype.reduceRight()`, `TypedArray.prototype.reverse()`, and `TypedArray.prototype.some()` are new in JavaScript runtime 2.0.

**ArrayBuffer**  
The following ES 6 methods on ArrayBuffer are supported:  
+ `isView()`
The following ES 6 prototype methods on ArrayBuffer are supported:  
+ `ArrayBuffer.prototype.slice()`

**Promise**  
The following ES 6 methods on promises are supported:  
+ `Promise.all()`
+ `Promise.allSettled()`
+ `Promise.any()`
+ `Promise.reject()`
+ `Promise.resolve()`
+ `Promise.race()`
**Note**  
`Promise.all()`, `Promise.allSettled()`, `Promise.any()`, and `Promise.race()` are new in JavaScript runtime 2.0.
The following ES 6 prototype methods on promises are supported:  
+ `Promise.prototype.catch()`
+ `Promise.prototype.finally()`
+ `Promise.prototype.then()`

**DataView**  
The following ES 6 prototype methods are supported:  
+ `DataView.prototype.getFloat32()`
+ `DataView.prototype.getFloat64()`
+ `DataView.prototype.getInt16()`
+ `DataView.prototype.getInt32()`
+ `DataView.prototype.getInt8()`
+ `DataView.prototype.getUint16()`
+ `DataView.prototype.getUint32()`
+ `DataView.prototype.getUint8()`
+ `DataView.prototype.setFloat32()`
+ `DataView.prototype.setFloat64()`
+ `DataView.prototype.setInt16()`
+ `DataView.prototype.setInt32()`
+ `DataView.prototype.setInt8()`
+ `DataView.prototype.setUint16()`
+ `DataView.prototype.setUint32()`
+ `DataView.prototype.setUint8()`
**Note**  
All Dataview ES 6 prototype methods are new in JavaScript runtime 2.0.

**Symbol**  
The following ES 6 methods are supported:  
+ `Symbol.for()`
+ `Symbol.keyfor()`
**Note**  
All Symbol ES 6 methods are new in JavaScript runtime 2.0.

**Text Decoder**  
The following prototype methods are supported:  
+ `TextDecoder.prototype.decode()`
The following prototype accessor properties are supported:  
+ `TextDecoder.prototype.encoding`
+ `TextDecoder.prototype.fatal`
+ `TextDecoder.prototype.ignoreBOM`

**Text Encoder**  
The following prototype methods are supported:  
+ `TextEncoder.prototype.encode()`
+ `TextEncoder.prototype.encodeInto()`

**Console**  
This is a helper object for debugging. It only supports the `log()` method, to record log messages.  
CloudFront Functions doesn't support comma syntax, such as `console.log('a', 'b')`. Instead, use the `console.log('a' + ' ' + 'b')` format.

## Error types
<a name="writing-functions-javascript-features-error-types-20"></a>

The following error objects are supported:
+ `Error`
+ `EvalError`
+ `InternalError`
+ `RangeError`
+ `ReferenceError`
+ `SyntaxError`
+ `TypeError`
+ `URIError`

## Globals
<a name="writing-functions-javascript-features-globals-20"></a>

The `globalThis` object is supported.

The following ES 5.1 global functions are supported:
+ `decodeURI()`
+ `decodeURIComponent()`
+ `encodeURI()`
+ `encodeURIComponent()`
+ `isFinite()`
+ `isNaN()`
+ `parseFloat()`
+ `parseInt()`

The following ES 6 global functions are supported:
+ `atob()`
+ `btoa()`
**Note**  
`atob()` and `btoa()` are new in JavaScript runtime 2.0.

The following global constants are supported:
+ `NaN`
+ `Infinity`
+ `undefined`
+ `arguments`

## Built-in modules
<a name="writing-functions-javascript-features-builtin-modules-20"></a>

The following built-in modules are supported.

**Topics**
+ [

### Buffer
](#writing-functions-javascript-features-builtin-modules-buffer-20)
+ [

### Query string
](#writing-functions-javascript-features-builtin-modules-query-string-20)
+ [

### Crypto
](#writing-functions-javascript-features-builtin-modules-crypto-20)

### Buffer
<a name="writing-functions-javascript-features-builtin-modules-buffer-20"></a>

The module provides the following methods:
+ `Buffer.alloc(size[, fill[, encoding]])`

  Allocate a `Buffer`.
  + `size`: Buffer size. Enter an integer.
  + `fill`: Optional. Enter a string, `Buffer`, Uint8Array, or integer. Default is `0`. 
  + `encoding`: Optional. When `fill` is a string, enter one of the following: `utf8`, `hex`, `base64`, `base64url`. Default is `utf8`.
+ `Buffer.allocUnsafe(size)`

  Allocate a non-initialized `Buffer`.
  + `size`: Enter an integer.
+ `Buffer.byteLength(value[, encoding])`

  Return the length of a value, in bytes.
  + `value`: A string, `Buffer`, TypedArray, Dataview, or Arraybuffer.
  + `encoding`: Optional. When `value` is a string, enter one of the following: `utf8`, `hex`, `base64`, `base64url`. Default is `utf8`.
+ `Buffer.compare(buffer1, buffer2)`

  Compare two `Buffer`s to help sort arrays. Returns `0` if they're the same, `-1` if `buffer1` comes first, or `1` if `buffer2` comes first.
  + `buffer1`: Enter a `Buffer`.
  + `buffer2`: Enter a different `Buffer`.
+ `Buffer.concat(list[, totalLength])`

  Concatenate multiple `Buffer`s. Returns `0` if none. Returns up to `totalLength`.
  + `list`: Enter a list of `Buffer`s. Note this will be truncated to `totalLength`.
  + `totalLength`: Optional. Enter an unsigned integer. Use sum of `Buffer` instances in list if blank.
+ `Buffer.from(array)`

  Create a `Buffer` from an array.
  + `array`: Enter a byte array from `0` to `255`. 
+ `Buffer.from(arrayBuffer, byteOffset[, length]))`

  Create a view from `arrayBuffer`, starting at offset `byteOffset` with length `length`.
  + `arrayBuffer`: Enter a `Buffer` array.
  + `byteOffset`: Enter an integer.
  + `length`: Optional. Enter an integer.
+ `Buffer.from(buffer)`

  Create a copy of the `Buffer`.
  + `buffer`: Enter a `Buffer`.
+ `Buffer.from(object[, offsetOrEncoding[, length]])`

  Create a `Buffer` from an object. Returns `Buffer.from(object.valueOf(), offsetOrEncoding, length)` if `valueOf()` is not equal to the object.
  + `object`: Enter an object.
  + `offsetOrEncoding`: Optional. Enter an integer or encoding string.
  + `length`: Optional. Enter an integer.
+ `Buffer.from(string[, encoding])`

  Create a `Buffer` from a string.
  + `string`: Enter a string.
  + `encoding`: Optional. Enter one of the following: `utf8`, `hex`, `base64`, `base64url`. Default is `utf8`.
+ `Buffer.isBuffer(object)`

  Check if `object` is a Buffer. Returns `true` or `false`.
  + `object`: Enter an object.
+ `Buffer.isEncoding(encoding)`

  Check if `encoding` is supported. Returns `true` or `false`.
  + `encoding`: Optional. Enter one of the following: `utf8`, `hex`, `base64`, `base64url`. Default is `utf8`.

The module provides the following buffer prototype methods:
+ `Buffer.prototype.compare(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]])`

  Compare `Buffer` with target. Returns `0` if they're the same, `1` if `buffer` comes first, or `-1` if `target` comes first.
  + `target`: Enter a `Buffer`.
  + `targetStart`: Optional. Enter an integer. Default is 0.
  + `targetEnd`: Optional. Enter an integer. Default is `target` length.
  + `sourceStart`: Optional. Enter an integer. Default is 0.
  + `sourceEnd`: Optional. Enter an integer. Default is `Buffer` length.
+ `Buffer.prototype.copy(target[, targetStart[, sourceStart[, sourceEnd]]])`

  Copy buffer to `target`.
  + `target`: Enter a `Buffer` or `Uint8Array`.
  + `targetStart`: Optional. Enter an integer. Default is 0.
  + `sourceStart`: Optional. Enter an integer. Default is 0.
  + `sourceEnd`: Optional. Enter an integer. Default is `Buffer` length.
+ `Buffer.prototype.equals(otherBuffer)`

  Compare `Buffer` to `otherBuffer`. Returns `true` or `false`.
  + `otherBuffer`: Enter a string.
+ `Buffer.prototype.fill(value[, offset[, end][, encoding])`

  Fill `Buffer` with `value`.
  + `value`: Enter a string, `Buffer`, or integer.
  + `offset`: Optional. Enter an integer.
  + `end`: Optional. Enter an integer.
  + `encoding`: Optional. Enter one of the following: `utf8`, `hex`, `base64`, `base64url`. Default is `utf8`.
+ `Buffer.prototype.includes(value[, byteOffset][, encoding])`

  Search for `value` in `Buffer`. Returns `true` or `false`.
  + `value`: Enter a string, `Buffer`, `Uint8Array`, or integer.
  + `byteOffset`: Optional. Enter an integer.
  + `encoding`: Optional. Enter one of the following: `utf8`, `hex`, `base64`, `base64url`. Default is `utf8`.
+ `Buffer.prototype.indexOf(value[, byteOffset][, encoding])`

  Search for first `value` in `Buffer`. Returns `index` if found; returns `-1` if not found.
  + `value`: Enter a string, `Buffer`, Unit8Array, or integer from 0 to 255. 
  + `byteOffset`: Optional. Enter an integer.
  + `encoding`: Optional. Enter one of the following if `value` is a string: `utf8`, `hex`, `base64`, `base64url`. Default is `utf8`.
+ `Buffer.prototype.lastIndexOf(value[, byteOffset][, encoding])`

  Search for last `value` in `Buffer`. Returns `index` if found; returns `-1` if not found.
  + `value`: Enter a string, `Buffer`, Unit8Array, or integer from 0 to 255. 
  + `byteOffset`: Optional. Enter an integer.
  + `encoding`: Optional. Enter one of the following if `value` is a string: `utf8`, `hex`, `base64`, `base64url`. Default is `utf8`.
+ `Buffer.prototype.readInt8(offset)`

  Read `Int8` at `offset` from `Buffer`.
  + `offset`: Enter an integer.
+ `Buffer.prototype.readIntBE(offset, byteLength)`

  Read `Int` as big-endian at `offset` from `Buffer`.
  + `offset`: Enter an integer.
  + `byteLength`: Optional. Enter an integer from `1` to `6`.
+ `Buffer.prototype.readInt16BE(offset)`

  Read `Int16` as big-endian at `offset` from `Buffer`.
  + `offset`: Enter an integer.
+ `Buffer.prototype.readInt32BE(offset)`

  Read `Int32` as big-endian at `offset` from `Buffer`.
  + `offset`: Enter an integer.
+ `Buffer.prototype.readIntLE(offset, byteLength)`

  Read `Int` as little-endian at `offset` from `Buffer`.
  + `offset`: Enter an integer.
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.readInt16LE(offset)`

  Read `Int16` as little-endian at `offset` from `Buffer`.
  + `offset`: Enter an integer.
+ `Buffer.prototype.readInt32LE(offset)`

  Read `Int32` as little-endian at `offset` from `Buffer`.
  + `offset`: Enter an integer.
+ `Buffer.prototype.readUInt8(offset)`

  Read `UInt8` at `offset` from `Buffer`.
  + `offset`: Enter an integer.
+ `Buffer.prototype.readUIntBE(offset, byteLength)`

  Read `UInt` as big-endian at `offset` from `Buffer`.
  + `offset`: Enter an integer.
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.readUInt16BE(offset)`

  Read `UInt16` as big-endian at `offset` from `Buffer`.
+ 
  + `offset`: Enter an integer.
+ `Buffer.prototype.readUInt32BE(offset)`

  Read `UInt32` as big-endian at `offset` from `Buffer`.
  + `offset`: Enter an integer.
+ `Buffer.prototype.readUIntLE(offset, byteLength)`

  Read `UInt` as little-endian at `offset` from `Buffer`.
  + `offset`: Enter an integer.
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.readUInt16LE(offset)`

  Read `UInt16` as little-endian at `offset` from `Buffer`.
  + `offset`: Enter an integer.
+ `Buffer.prototype.readUInt32LE(offset)`

  Read `UInt32` as little-endian at `offset` from `Buffer`.
  + `offset`: Enter an integer.
+ `Buffer.prototype.readDoubleBE([offset])`

  Read a 64-bit double as big-endian at `offset` from `Buffer`.
  + `offset`: Optional. Enter an integer.
+ `Buffer.prototype.readDoubleLE([offset])`

  Read a 64-bit double as little-endian at `offset` from `Buffer`.
  + `offset`: Optional. Enter an integer.
+ `Buffer.prototype.readFloatBE([offset])`

  Read a 32-bit float as big-endian at `offset` from `Buffer`.
  + `offset`: Optional. Enter an integer.
+ `Buffer.prototype.readFloatLE([offset])`

  Read a 32-bit float as little-endian at `offset` from `Buffer`.
  + `offset`: Optional. Enter an integer.
+ `Buffer.prototype.subarray([start[, end]])`

  Returns a copy of `Buffer` that is offset and cropped with a new `start` and `end`.
  + `start`: Optional. Enter an integer. Default is 0.
  + `end`: Optional. Enter an integer. Default is buffer length.
+ `Buffer.prototype.swap16()`

  Swap the `Buffer` array byte order, treating it as an array of 16-bit numbers. `Buffer` length must be divisible by 2, or you will receive an error.
+ `Buffer.prototype.swap32()`

  Swap the `Buffer` array byte order, treating it as an array of 32-bit numbers . `Buffer` length must be divisible by 4, or you will receive an error.
+ `Buffer.prototype.swap64()`

  Swap the `Buffer` array byte order, treating it as an array of 64-bit numbers. `Buffer` length must be divisible by 8, or you will receive an error.
+ `Buffer.prototype.toJSON()`

  Returns `Buffer` as a JSON. 
+ `Buffer.prototype.toString([encoding[, start[, end]]])`

  Convert `Buffer`, from `start` to `end`, to encoded string.
  + `encoding`: Optional. Enter one of the following: `utf8`, `hex`, `base64`, or `base64url`. Default is `utf8`.
  + `start`: Optional. Enter an integer. Default is 0.
  + `end`: Optional. Enter an integer. Default is buffer length.
+ `Buffer.prototype.write(string[, offset[, length]][, encoding])`

  Write encoded `string` to `Buffer` if there is space, or a truncated `string` if there is not enough space.
  + `string`: Enter a string.
  + `offset`: Optional. Enter an integer. Default is 0.
  + `length`: Optional. Enter an integer. Default is the length of the string.
  + `encoding`: Optional. Optionally enter one of the following: `utf8`, `hex`, `base64`, or `base64url`. Default is `utf8`.
+ `Buffer.prototype.writeInt8(value, offset, byteLength)`

  Write `Int8` `value` of `byteLength` at `offset` to `Buffer`.
  + `value`: Enter an integer.
  + `offset`: Enter an integer
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeIntBE(value, offset, byteLength)`

  Write `value` at `offset` to `Buffer`, using big-endian.
  + `value`: Enter an integer.
  + `offset`: Enter an integer
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeInt16BE(value, offset, byteLength)`

  Write `value` at `offset` to `Buffer`, using big-endian.
  + `value`: Enter an integer.
  + `offset`: Enter an integer
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeInt32BE(value, offset, byteLength)`

  Write `value` at `offset` to `Buffer`, using big-endian.
  + `value`: Enter an integer.
  + `offset`: Enter an integer
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeIntLE(offset, byteLength)`

  Write `value` at `offset` to `Buffer`, using little-endian.
  + `offset`: Enter an integer.
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeInt16LE(offset, byteLength)`

  Write `value` at `offset` to `Buffer`, using little-endian.
  + `offset`: Enter an integer.
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeInt32LE(offset, byteLength)`

  Write `value` at `offset` to `Buffer`, using little-endian.
  + `offset`: Enter an integer.
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeUInt8(value, offset, byteLength)`

  Write `UInt8` `value` of `byteLength` at `offset` to `Buffer`.
  + `value`: Enter an integer.
  + `offset`: Enter an integer
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeUIntBE(value, offset, byteLength)`

  Write `value` at `offset` to `Buffer`, using big-endian.
  + `value`: Enter an integer.
  + `offset`: Enter an integer
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeUInt16BE(value, offset, byteLength)`

  Write `value` at `offset` to `Buffer`, using big-endian.
  + `value`: Enter an integer.
  + `offset`: Enter an integer
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeUInt32BE(value, offset, byteLength)`

  Write `value` at `offset` to `Buffer`, using big-endian.
  + `value`: Enter an integer.
  + `offset`: Enter an integer
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeUIntLE(value, offset, byteLength)`

  Write `value` at `offset` to `Buffer`, using little-endian.
  + `value`: Enter an integer.
  + `offset`: Enter an integer
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeUInt16LE(value, offset, byteLength)`

  Write `value` at `offset` to `Buffer`, using little-endian.
  + `value`: Enter an integer.
  + `offset`: Enter an integer
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeUInt32LE(value, offset, byteLength)`

  Write `value` at `offset` to `Buffer`, using little-endian.
  + `value`: Enter an integer.
  + `offset`: Enter an integer
  + `byteLength`: Enter an integer from `1` to `6`.
+ `Buffer.prototype.writeDoubleBE(value, [offset])`

  Write `value` at `offset` to `Buffer`, using big-endian.
  + `value`: Enter an integer.
  + `offset`: Optional. Enter an integer. Default is 0.
+ `Buffer.prototype.writeDoubleLE(value, [offset])`

  Write `value` at `offset` to `Buffer`, using little-endian.
  + `value`: Enter an integer.
  + `offset`: Optional. Enter an integer. Default is 0.
+ `Buffer.prototype.writeFloatBE(value, [offset])`

  Write `value` at `offset` to `Buffer`, using big-endian.
  + `value`: Enter an integer.
  + `offset`: Optional. Enter an integer. Default is 0.
+ `Buffer.prototype.writeFloatLE(value, [offset])`

  Write `value` at `offset` to `Buffer`, using little-endian.
  + `value`: Enter an integer.
  + `offset`: Optional. Enter an integer. Default is 0.

The following instance methods are supported:
+ `buffer[index]`

  Get and set octet (byte) at `index` in `Buffer`. 
  + Get a number from `0` to `255`. Or set a number from from `0` to `255`.

The following instance properties are supported:
+ `buffer`

  Get the `ArrayBuffer` object for the buffer. 
+ `byteOffset`

  Get the `byteOffset` of the buffer's `Arraybuffer` object.
+ `length`

  Get the buffer byte count.

**Note**  
All Buffer module methods are new in JavaScript runtime 2.0.

### Query string
<a name="writing-functions-javascript-features-builtin-modules-query-string-20"></a>

**Note**  
The [CloudFront Functions event object](functions-event-structure.md) automatically parses URL query strings for you. That means that in most cases you don’t need to use this module.

The query string module (`querystring`) provides methods for parsing and formatting URL query strings. You can load the module using `require('querystring')`. The module provides the following methods.

`querystring.escape(string)`  
URL-encodes the given `string`, returning an escaped query string. The method is used by `querystring.stringify()` and should not be used directly.

`querystring.parse(string[, separator[, equal[, options]]])`  
Parses a query string (`string`) and returns an object.  
The `separator` parameter is a substring for delimiting key and value pairs in the query string. By default it is `&`.  
The `equal` parameter is a substring for delimiting keys and values in the query string. By default it is `=`.  
The `options` parameter is an object with the following keys:    
`decodeURIComponent function`  
A function to decode percent-encoded characters in the query string. By default it is `querystring.unescape()`.  
`maxKeys number`  
The maximum number of keys to parse. By default it is `1000`. Use a value of `0` to remove the limitations for counting keys.
By default, percent-encoded characters within the query string are assumed to use the UTF-8 encoding. Invalid UTF-8 sequences are replaced with the `U+FFFD` replacement character.  
For example, for the following query string:  

```
'name=value&abc=xyz&abc=123'
```
The return value of `querystring.parse()` is:  

```
{
name: 'value',
abc: ['xyz', '123']
}
```
`querystring.decode()` is an alias for `querystring.parse()`.

`querystring.stringify(object[, separator[, equal[, options]]])`  
Serializes an `object` and returns a query string.  
The `separator` parameter is a substring for delimiting key and value pairs in the query string. By default it is `&`.  
The `equal` parameter is a substring for delimiting keys and values in the query string. By default it is `=`.  
The `options` parameter is an object with the following keys:    
`encodeURIComponent function`  
The function to use for converting URL-unsafe characters to percent-encoding in the query string. By default it is `querystring.escape()`.
By default, characters that require percent-encoding within the query string are encoded as UTF-8. To use a different encoding, specify the `encodeURIComponent` option.  
For example, for the following code:  

```
querystring.stringify({ name: 'value', abc: ['xyz', '123'], anotherName: '' });
```
The return value is:  

```
'name=value&abc=xyz&abc=123&anotherName='
```
`querystring.encode()` is an alias for `querystring.stringify()`.

`querystring.unescape(string)`  
Decodes URL percent-encoded characters in the given `string`, returning an unescaped query string. This method is used by `querystring.parse()` and should not be used directly.

### Crypto
<a name="writing-functions-javascript-features-builtin-modules-crypto-20"></a>

The cryptographic module (`crypto`) provides standard hashing and hash-based message authentication code (HMAC) helpers. You can load the module using `require('crypto')`.

**Hashing methods**

`crypto.createHash(algorithm)`  
Creates and returns a hash object that you can use to generate hash digests using the given algorithm: `md5`, `sha1`, or `sha256`.

`hash.update(data)`  
Updates the hash content with the given `data`.

`hash.digest([encoding])`  
Calculates the digest of all of the data passed using `hash.update()`. The encoding can be `hex`, `base64`, or `base64url`.

**HMAC methods**

`crypto.createHmac(algorithm, secret key)`  
Creates and returns an HMAC object that uses the given `algorithm` and `secret key`. The algorithm can be `md5`, `sha1`, or `sha256`.

`hmac.update(data)`  
Updates the HMAC content with the given `data`.

`hmac.digest([encoding])`  
Calculates the digest of all of the data passed using `hmac.update()`. The encoding can be `hex`, `base64`, or `base64url`.

## Restricted features
<a name="writing-functions-javascript-features-restricted-features-20"></a>

The following JavaScript language features are either unsupported or restricted due to security concerns.

**Dynamic code evaluation**  
Dynamic code evaluation is not supported. Both `eval()` and `Function` constructors throw an error if attempted. For example, `const sum = new Function('a', 'b', 'return a + b')` throws an error.

**Timers**  
The `setTimeout()`, `setImmediate()`, and `clearTimeout()` functions are not supported. There is no provision to defer or yield within a function run. Your function must synchronously run to completion.

**Date and timestamps**  
For security reasons, there is no access to high-resolution timers. All `Date` methods to query the current time always return the same value during the lifetime of a single function run. The returned timestamp is the time when the function started running. Consequently, you cannot measure elapsed time in your function.

**File system access**  
There is no file system access. For example, there is no `fs` module for file system access like there is in Node.js.

**Process access**  
There is no process access. For example, there is no `process` global object for processing information access like there is in Node.js.

**Environment variables**  
There is no access to environment variables. Instead, you can use CloudFront KeyValueStore to create a centralized datastore of key-value pairs for your CloudFront Functions. CloudFront KeyValueStore enables dynamic updates to your configuration data without needing to deploy code changes. For more information, see [Amazon CloudFront KeyValueStore](kvs-with-functions.md).

**Network access**  
There is no support for network calls. For example, XHR, HTTP(S), and socket are not supported.

# Helper methods for key value stores
<a name="functions-custom-methods"></a>

**Note**  
Key value store helper method calls from CloudFront Functions don't trigger an AWS CloudTrail data event. These events aren't logged in the CloudTrail event history. For more information, see [Logging Amazon CloudFront API calls using AWS CloudTrail](logging_using_cloudtrail.md).

This section applies if you use the [CloudFront Key Value Store](kvs-with-functions.md) to include key values in the function that you create. CloudFront Functions has a module that provides three helper methods to read values from the key value store.

To use this module in the function code, make sure that you have [associated a key value store](kvs-with-functions-associate.md) with the function. 

Next, include the following statements in the first lines of the function code:

```
import cf from 'cloudfront';
const kvsHandle = cf.kvs();
```



## `get()` method
<a name="functions-custom-methods-get"></a>

Use this method to return the key value for the key name that you specify. 

**Request**

```
get("key", options);
```
+ `key`: The name of the key whose value needs to be fetched
+ `options`: There is one option, `format`. It ensures that the function parses the data correctly. Possible values:
  + `string`: (Default) UTF8 encoded
  + `json` 
  + `bytes`: Raw binary data buffer

**Request example**

```
const value = await kvsHandle.get("myFunctionKey", { format: "string"});
```

**Response**

The response is a `promise` that resolves to a value in the format requested by using `options`. By default, the value is returned as a string.

### Error handling
<a name="error-handling-exists-method"></a>

The `get()` method will return an error when the key that you requested doesn't exist in the associated key value store. To manage this use case, you can add a `try` and `catch` block to your code.

**Warning**  
Using promise combinators (for example, `Promise.all`, `Promise.any`, and promise chain methods (for example, `then` and `catch`) can require high function memory usage. If your function exceeds the [maximum function memory](cloudfront-limits.md#limits-functions) quota, it will fail to execute. To avoid this error, we recommend that you use the `await` syntax sequentially or in loops to request multiple values.  
**Example**  

```
var value1 = await kvs.get('key1');
var value2 = await kvs.get('key2');
```
Currently, using promise combinators to get multiple values won't improve performance, such as the following example.  

```
var values = await Promise.all([kvs.get('key1'), kvs.get('key2'),]);
```

## `exists()` method
<a name="functions-custom-methods-exists"></a>

Use this method to identify whether or not the key exists in the key value store.

**Request**

```
exists("key");
```

**Request example**

```
const exist = await kvsHandle.exists("myFunctionkey");
```

**Response**

The response is a `promise` that returns a Boolean (`true` or `false`). This value specifies whether or not the key exists in the key value store.

## `meta()` method
<a name="functions-custom-methods-meta"></a>

Use this method to return metadata about the key value store.

**Request**

```
meta();
```

**Request example**

```
const meta = await kvsHandle.meta();
```

**Response**

The response is a `promise` that resolves to an object with the following properties:
+ `creationDateTime`: The date and time that the key value store was created, in ISO 8601 format.
+ `lastUpdatedDateTime`: The date and time that the key value store was last synced from the source, in ISO 8601 format. The value doesn't include the propagation time to the edge.
+ `keyCount`: The total number of keys in the KVS after the last sync from the source.

**Response example**

```
{keyCount:3,creationDateTime:2023-11-30T23:07:55.765Z,lastUpdatedDateTime:2023-12-15T03:57:52.411Z}
```

# Helper methods for origin modification
<a name="helper-functions-origin-modification"></a>

This section applies if you dynamically update or change the origin used on the request inside your CloudFront Functions code. You can update the origin on *viewer request* CloudFront Functions only. CloudFront Functions has a module that provides helper methods to dynamically update or change the origin.

To use this module, create a CloudFront function using JavaScript runtime 2.0 and include the following statement in the first line of the function code:

```
import cf from 'cloudfront';
```

For more information, see [JavaScript runtime 2.0 features for CloudFront Functions](functions-javascript-runtime-20.md).

**Note**  
The Test API and Test console pages don't test whether an origin modification has occurred. However, testing ensures that the function code executes without error.

## Choose between CloudFront Functions and Lambda@Edge
<a name="origin-modification-considerations"></a>

You can update your origins by using either CloudFront Functions or Lambda@Edge.

When using CloudFront Functions to update origins, you use the *viewer request* event trigger, which means this logic will run on every request when this function is used. When using Lambda@Edge, the origin updating capabilities are on the *origin request* event trigger, which means this logic only runs on cache misses.

Your choice depends largely on your workload and any existing usage of CloudFront Functions and Lambda@Edge on your distributions. The following considerations can help you decide whether to use CloudFront Functions or Lambda@Edge to update your origins.

CloudFront Functions is most useful in the following situations:
+ When your requests are dynamic (meaning they cannot be cached) and will always go to origin. CloudFront Functions provides better performance and lower overall cost.
+ When you already have an existing viewer request CloudFront function that will run on every request, you can add the origin updating logic into the existing function.

To use CloudFront Functions to update origins, see the helper methods in the following topics.

Lambda@Edge is most useful in the following situations:
+ When you have highly cacheable content, Lambda@Edge can be more cost-efficient because it runs only on cache misses, while CloudFront Functions runs on every request.
+ When you already have an existing origin request Lambda@Edge function, you can add the origin updating logic into the existing function.
+ When your origin update logic requires fetching data from third-party data sources, such as Amazon DynamoDB or Amazon S3.

For more information about Lambda@Edge, see [Customize at the edge with Lambda@Edge](lambda-at-the-edge.md).

## updateRequestOrigin() method
<a name="update-request-origin-helper-function"></a>

Use the `updateRequestOrigin()` method to update the origin settings for a request. You can use this method to update existing origin properties for origins that are already defined in your distribution, or to define a new origin for the request. To do so, specify the properties that you want to change.

**Important**  
Any settings that you don't specify in the `updateRequestOrigin()` will inherit the *same settings* from the existing origin's configuration.

The origin set by the `updateRequestOrigin()` method can be any HTTP endpoint and doesn't need to be an existing origin within your CloudFront distribution.

**Notes**  
If you're updating an origin that is part of an origin group, only the *primary origin* of the origin group is updated. The secondary origin remains unchanged. Any response code from the modified origin that matches the failover criteria will trigger a failover to the secondary origin.
If you are changing the origin type and have OAC enabled, make sure that the origin type in `originAccessControlConfig` matches the new origin type.
You can't use the `updateRequestOrigin()` method to update [VPC origins](private-content-vpc-origins.md). The request will fail.

**Request**

```
updateRequestOrigin({origin properties})
```

The `origin properties` can contain the following:

**domainName (optional)**  
The domain name of the origin. If this is not provided, the domain name from the assigned origin is used instead.    
**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 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.

**hostHeader (optional, for non-S3 custom origins)**  
The host header to use when making the request to the origin. If this is not provided, the value from the domainName parameter is used. If neither host header or domain name parameter are provided, the domain name from the assigned origin is used or the host header from the incoming request if the forward to origin (FTO) policy includes the host. The host header can't include a colon (`:`) and can't be an IP address. The host header can be up to 253 characters.

**originPath (optional)**  
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/`. If this is not provided, then the origin path from the assigned origin is used.    
**For custom origins**  
The path should be URL encoded and have a maximum length of 255 characters.

**customHeaders (optional)**  
You can include custom headers with the request by specifying a header name and value pair for each custom header. The format is different than that of the request and response headers in the event structure. Use the following key-value pair syntax:  

```
{"key1": "value1", "key2": "value2", ...}
```
You can't add headers that are disallowed, and a header with the same name can't also be present in the incoming request `headers`. Header name must be lowercase in your function code. When CloudFront Functions converts the event object back into an HTTP request, the first letter of each word in header names is capitalized, and words are separated by a hyphen.  
For example, if you function code adds a header named `example-header-name`, CloudFront converts this to `Example-Header-Name` in the HTTP request. 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).  
If this is not provided, then any custom headers from the assigned origin are used.

**connectionAttempts (optional)**  
The number of times that CloudFront attempts to connect to the origin. The minimum is 1 and the maximum is 3. If this is not provided, the connection attempts from the assigned origin are used.

**originShield (optional)**  
This enables or updates CloudFront Origin Shield. Using Origin Shield can help reduce the load on your origin. For more information, see [Use Amazon CloudFront Origin Shield](origin-shield.md). If this is not provided, the Origin Shield settings from the assigned origin are used.    
**enabled (required)**  
Boolean expression to enable or disable Origin Shield. Accepts a `true` or `false` value.  
**region (required when enabled)**  
The AWS Region for Origin Shield. Specify the AWS Region that has the lowest latency to your origin. Use the Region code, not the Region name. For example, use `us-east-2` to specify the US East (Ohio) Region.  
When you enable CloudFront Origin Shield, you must specify the AWS Region for it. For a list of available AWS Regions and help choosing the best Region for your origin, see [Choose the AWS Region for Origin Shield](origin-shield.md#choose-origin-shield-region).

**originAccessControlConfig (optional)**  
The unique identifier of an origin access control (OAC) for this origin. This is only used when the origin supports a CloudFront OAC, such as Amazon S3, Lambda function URLs, MediaStore, and MediaPackage V2. If this is not provided, then the OAC settings from the assigned origin are used.  
This does not support the legacy origin access identity (OAI). For more information, see [Restrict access to an AWS origin](private-content-restricting-access-to-origin.md).    
**enabled (required)**  
Boolean expression to enable or disable OAC. Accepts a `true` or `false` value.  
**signingBehavior (required when enabled)**  
Specifies which requests CloudFront signs (adds authentication information to). Specify `always` for the most common use case. For more information, see [Advanced settings for origin access control](private-content-restricting-access-to-s3.md#oac-advanced-settings-s3).   
This field can have one of the following values:  
+ `always` – CloudFront signs all origin requests, overwriting the `Authorization` header from the viewer request if one exists.
+ `never` – CloudFront doesn’t sign any origin requests. This value turns off origin access control for the origin.
+ `no-override` – If the viewer request doesn’t contain the `Authorization` header, then CloudFront signs the origin request. If the viewer request contains the `Authorization` header, then CloudFront doesn’t sign the origin request and instead passes along the `Authorization` header from the viewer request.
**Warning**  
To pass along the `Authorization` header from the viewer request, you must add it to an origin request policy for all cache behaviors that use origins associated with this origin access control. For more information, see [Control origin requests with a policy](controlling-origin-requests.md).  
**signingProtocol (required when enabled)**  
The signing protocol of the OAC, which determines how CloudFront signs (authenticates) requests. The only valid value is `sigv4`.  
**originType (required when enabled)**  
The type of origin for this OAC. Valid values include `s3`, `mediapackagev2`, `mediastore`, and `lambda`. 

**timeouts (optional)**  
Timeouts that you can specify for how long CloudFront should attempt to wait for origins to respond or send data. If this is not provided, then the timeout settings from the assigned origin are used.   
Unless specified, these timeouts support both custom origins and Amazon S3 origins.   
**readTimeout (optional)**  
The `readTimeout` applies to both of the following values:  
+ How long (in seconds) CloudFront waits for a response after forwarding a request to the origin.
+ How long (in seconds) CloudFront waits after receiving a packet of a response from the origin and before receiving the next packet. 
The minimum timeout is 1 second and the maximum is 120 seconds. For more information, see [Response timeout](DownloadDistValuesOrigin.md#DownloadDistValuesOriginResponseTimeout).  
**responseCompletionTimeout (optional)**  
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`. For more information, see [Response completion timeout](DownloadDistValuesOrigin.md#response-completion-timeout).  
**keepAliveTimeout (optional)**  
This timeout only applies to custom origins, not Amazon S3 origins. (S3 origin configurations will ignore these settings.)   
The `keepAliveTimeout` specifies how long CloudFront should try to maintain the connection to the origin after receiving the last packet of the response. The minimum timeout is 1 second and the maximum is 120 seconds. For more information, see [Keep-alive timeout (custom and VPC origins only)](DownloadDistValuesOrigin.md#DownloadDistValuesOriginKeepaliveTimeout).  
**connectionTimeout (optional)**  
The number of seconds that CloudFront waits when trying to establish a connection to the origin. The minimum timeout is 1 second and the maximum is 10 seconds. For more information, see [Connection timeout](DownloadDistValuesOrigin.md#origin-connection-timeout).

**customOriginConfig (optional)**  
Use `customOriginConfig` to specify connection settings for origins that are *not* an Amazon S3 bucket. There is one exception: you can specify these settings if the S3 bucket is configured with static website hosting. (Other types of S3 bucket configurations will ignore these settings.) If `customOriginConfig` is not provided, then the settings from the assigned origin are used.    
**port (required)**  
The HTTP port that CloudFront uses to connect to the origin. Specify the HTTP port that the origin listens on.   
**protocol (required)**  
Specifies the protocol (HTTP or HTTPS) that CloudFront uses to connect to the origin. Valid values are as follows:  
+ `http` – CloudFront always uses HTTP to connect to the origin
+ `https` – CloudFront always uses HTTPS to connect to the origin  
**sslProtocols (required)**  
A list that specifies the minimum SSL/TLS protocol that CloudFront uses when connecting to your origin over HTTPS. Valid values include `SSLv3`, `TLSv1`, `TLSv1.1`, and `TLSv1.2`. For more information, see [Minimum origin SSL protocol](DownloadDistValuesOrigin.md#DownloadDistValuesOriginSSLProtocols).  
**ipAddressType (optional)**  
Specifies the IP address type that CloudFront uses to connect to the origin. Valid values include `ipv4`, `ipv6`, and `dualstack`. Changing `ipAddressType` is only supported when the `domainName` property is also being changed.

**sni (optional, for non-S3 custom origins)**  
The Server Name Indication (SNI) is an extension to the Transport Layer Security (TLS) protocol by which a client indicates which hostname it's attempting to connect to at the start of the TLS handshake process. This value should match a common name on a TLS certificate on your origin server. Otherwise, your origin server may throw an error.   
If this is not provided, the value from the `hostHeader` parameter is used. If the host header not provided, the value from the `domainName` parameter is used.  
If neither host header or domain name parameter are provided, the domain name from the assigned origin is used or the host header from the incoming request if the forward to origin (FTO) policy includes the host. The SNI can't include a colon (`:`) and can't be an IP address. The SNI can be up to 253 characters.

**allowedCertificateNames (optional, for non-S3 custom origins)**  
You can include a list of valid certificate names to be used by CloudFront to validate the domain matching from your origin server TLS certificate during the TLS handshake with your origin server. This field expects an array of valid domain names and can include wildcard domains, such as `*.example.com`.   
You can specify up to 20 allowed certificate names. Each certificate name can have up to 64 characters.

**Example – Update to Amazon S3 request origin**  
The following example changes the viewer request’s origin to an S3 bucket, enables OAC, and resets custom headers sent to the origin.  

```
cf.updateRequestOrigin({
    "domainName" : "amzn-s3-demo-bucket-in-us-east-1.s3.us-east-1.amazonaws.com",
    "originAccessControlConfig": {
        "enabled": true,
        "signingBehavior": "always",
        "signingProtocol": "sigv4",
        "originType": "s3"
    },
    // Empty object resets any header configured on the assigned origin
    "customHeaders": {}
});
```

**Example – Update to Application Load Balancer request origin**  
The following example changes the viewer request’s origin to an Application Load Balancer origin and sets a custom header and timeouts.  

```
cf.updateRequestOrigin({
    "domainName" : "example-1234567890.us-east-1.elb.amazonaws.com",
    "timeouts": {
        "readTimeout": 30,
        "connectionTimeout": 5
    },
    "customHeaders": {
        "x-stage": "production",
        "x-region": "us-east-1"
    }
});
```

**Example – Update to origin with Origin Shield enabled**  
In the following example, the origin in the distribution has Origin Shield enabled. The function code updates only the domain name used for the origin and omits all the other optional parameters. In this case, Origin Shield will still be used with the modified origin domain name because the Origin Shield parameters were not updated.  

```
cf.updateRequestOrigin({
    "domainName" : "www.example.com"
});
```

**Example – Update the host header, SNI, and allowed certificate names**  
For most use cases, you won't need to use this type of modification to requests going to your origin. These parameters shouldn't be used unless you understand the impact of changing these values. 
The following example changes the domain name, the host header, SNI, and allowed certificates on the request to the origin.   

```
cf.updateRequestOrigin({ 
    "domainName": "www.example.com", 
    "hostHeader": "test.example.com", 
    "sni": "test.example.net", 
    "allowedCertificateNames": ["*.example.com", "*.example.net"],
});
```

## selectRequestOriginById() method
<a name="select-request-origin-id-helper-function"></a>

Use `selectRequestOriginById()` to update an existing origin by selecting a different origin that's already configured in your distribution. This method uses all the same settings that are defined by the updated origin.

This method only accepts origins that are already defined in the same distribution used when running the function. Origins are referenced by the origin ID, which is the origin name that you defined when setting up the origin.

If you have a VPC origin configured in your distribution, you can use this method to update your origin to your VPC origin. For more information, see [Restrict access with VPC origins](private-content-vpc-origins.md).

**Notes**  
The `selectRequestOriginById()` function cannot select an origin that has mutual TLS (origin) enabled. Attempting to select a mutual TLS (origin) enabled origin using this function will result in a validation error.
If your use case requires dynamic origin selection with mutual TLS (origin), use `updateRequestOrigin()` instead, ensuring all target origins use the same client certificate.

**Request**

```
cf.selectRequestOriginById(origin_id, {origin_overrides})
```

In the previous example, `origin_id` is a string that points to the origin name of an origin in the distribution that's running the function. The `origin_overrides `parameter can contain the following:

**hostHeader (optional, for non-S3 custom origins)**  
The host header to use when making the request to the origin. If this is not provided, the value from the `domainName` parameter is used.   
If neither host header or domain name parameter are provided, the domain name from the assigned origin is used or the host header from the incoming request if the forward to origin (FTO) policy includes the host. The host header can't include a colon (`:`) and can't be an IP address. The host header can be up to 253 characters.

**sni (optional, for non-S3 custom origins)**  
The Server Name Indication (SNI) is an extension to the Transport Layer Security (TLS) protocol by which a client indicates which hostname it's attempting to connect to at the start of the TLS handshake process. This value should match a common name on a TLS certificate on your origin server. Otherwise, your origin server may throw an error.   
If this is not provided, the value from the `hostHeader` parameter is used. If the host header not provided, the value from the `domainName` parameter is used.   
If neither host header or domain name parameter are provided, the domain name from the assigned origin is used or the host header from the incoming request if the forward to origin (FTO) policy includes the host. The SNI can't include a colon (`:`) and can't be an IP address. The SNI can be up to 253 characters.

**allowedCertificateNames (optional, for non-S3 custom origins)**  
You can include a list of valid certificate names to be used by CloudFront to validate the domain matching from your origin server TLS certificate during the TLS handshake with your origin server. This field expects an array of valid domain names and can include wildcard domains, such as `*.example.com`.   
You can specify up to 20 allowed certificate names. Each certificate name can have up to 64 characters.

**Request**

```
selectRequestOriginById(origin_id)
```

In the preceding example, `origin_id` is a string that points to the origin name of an origin in the distribution that's running the function.

**Example – Select Amazon S3 request origin**  
The following example selects the origin named `amzn-s3-demo-bucket-in-us-east-1` from the list of origins associated with the distribution, and applies the configuration settings of the `amzn-s3-demo-bucket-in-us-east-1` origin to the request.  

```
cf.selectRequestOriginById("amzn-s3-demo-bucket-in-us-east-1");
```

**Example – Select Application Load Balancer request origin**  
The following example selects an Application Load Balancer origin named `myALB-prod` from the list of origins associated with the distribution, and applies the configuration settings of `myALB-prod` to the request.  

```
cf.selectRequestOriginById("myALB-prod");
```

**Example – Select Application Load Balancer request origin and override the host header**  
Like the previous example, the following example selects an Application Load Balancer origin named `myALB-prod` from the list of origins associated with the distribution, and applies the configuration settings of `myALB-prod` to the request. However, this example overrides the host header value using `origin_overrides`.  

```
cf.overrideRequestOrigin("myALB-prod",{ 
        "hostHeader" : "test.example.com"
});
```

## createRequestOriginGroup() method
<a name="create-request-origin-group-helper-function"></a>

Use `createRequestOriginGroup()` to define two origins to use as an [origin group](high_availability_origin_failover.md#concept_origin_groups.creating) for failover in scenarios that require high availability.

An origin group includes two origins (a primary and a secondary) and a failover criteria that you specify. You create an origin group to support origin failover in CloudFront. When you create or update an origin group using this method, you can specify the origin group instead of a single origin. CloudFront will failover from the primary origin to the secondary origin, using the failover criteria.

If you have a VPC origin configured in your distribution, you can use this method to create an origin group using a VPC origin. For more information, see [Restrict access with VPC origins](private-content-vpc-origins.md).

**Notes**  
The `createRequestOriginGroup()` function does not support creating origin groups that include Mutual TLS (origin) enabled origins. Origin groups with Mutual TLS (origin) origins cannot be created dynamically through CloudFront Functions.
If you need origin failover capabilities with Mutual TLS (origin), configure origin groups directly in your CloudFront distribution settings rather than creating them dynamically in functions.

### Request
<a name="create-origin-group-request"></a>

```
createRequestOriginGroup({origin_group_properties})
```

In the preceding example, the `origin_group_properties` can contain the following:

**originIds (required)**  
Array of `origin_ids`, where the `origin_id` is a string that points to the origin name of an origin in the distribution running the function. You must provide two origins as part of the array. The first origin in the list is the primary origin and the second serves as the second origin for failover purposes. 

**originOverrides (optional)**  
 A few advanced settings are allowed to be overwritten by using the `{origin_overrides}` parameter. The `origin overrides` can contain the following:     
**hostHeader (optional, for non-S3 custom origins)**  
The host header to use when making the request to the origin. If this is not provided, the value from the `domainName` parameter is used.   
If neither host header or domain name parameter are provided, the domain name from the assigned origin is used or the host header from the incoming request if the forward to origin (FTO) policy includes the host. The host header can't include a colon (`:`) and can't be an IP address. The host header can be up to 253 characters.  
**sni (optional, for non-S3 custom origins)**  
The Server Name Indication (SNI) is an extension to the Transport Layer Security (TLS) protocol by which a client indicates which hostname it is attempting to connect to at the start of the TLS handshaking process. This value should match a common name on a TLS certificate on your origin server, otherwise your origin server may throw an error.   
If this is not provided, the value from the `hostHeader` parameter is used. If the host header not provided, the value from the `domainName` parameter is used.  
If neither host header or domain name parameter are provided, the domain name from the assigned origin is used or the host header from the incoming request if the forward to origin (FTO) policy includes the host. The SNI can't include a colon (`:`) and can't be an IP address. The SNI can be up to 253 characters.  
**allowedCertificateNames (optional, for non-S3 custom origins)**  
You can include a list of valid certificate names to be used by CloudFront to validate the domain matching from your origin server TLS certificate during the TLS handshake with your origin server. This field expects an array of valid domain names and can include wildcard domains, such as `*.example.com`.   
You can specify up to 20 allowed certificate names. Each certificate name can have up to 64 characters.

**selectionCriteria (optional)**  
Select whether to use the `default` origin failover criteria or to use the `media-quality-score` based failover logic. Valid values are as follows:  
+ `default` uses the failover criteria, based on the status codes that are specified in the `failoverCriteria`. If you don't set `selectionCriteria` in the function, `default` will be used.
+ `media-quality-score` is used when the media aware routing capability is being used.

**failoverCriteria (required)**  
An array of status codes that, when returned from the primary origin, will trigger CloudFront to failover to the secondary origin. If you overwrite an existing origin group, this array will overwrite all failover status codes that are set in the origin group's original configuration.  
When you use `media-quality-score` `selectionCriteria`, CloudFront will attempt to route requests based on the media quality score. If the selected origin returns an error code set in this array, CloudFront will failover to the other origin.

**Example – Create request origin group**  
The following example creates an origin group for a request using the origin IDs. These origin IDs come from the origin group configuration for the distribution used to run this function.  
Optionally, you can use `originOverrides` to override the origin group configurations for `sni`, `hostHeader`, and `allowedCertificateNames`.  

```
import cf from 'cloudfront';

function handler(event) {
    cf.createRequestOriginGroup({
        "originIds": [
            {
                "originId": "origin-1",
                "originOverrides": {
                    "hostHeader": "hostHeader.example.com",
                    "sni": "sni.example.com",
                    "allowedCertificateNames": ["cert1.example.com", "cert2.example.com", "cert3.example.com"]
                }
            },
            {
                "originId": "origin-2",
                "originOverrides": {
                    "hostHeader": "hostHeader2.example.com",
                    "sni": "sni2.example.com",
                    "allowedCertificateNames": ["cert4.example.com", "cert5.example.com"]
                }
            }
        ],
        "failoverCriteria": {
            "statusCodes": [500]
        }
    });
    
    event.request.headers['x-hookx'] = { value: 'origin-overrides' };
    return event.request;
}
```

# Helper methods for CloudFront SaaS Manager properties
<a name="saas-specific-logic-function-code"></a>

Use the following helper functions for CloudFront SaaS Manager to retrieve values for your multi-tenant distributions in the function that you create. To use examples on this page, you must first create a CloudFront function by using JavaScript runtime 2.0. For more information, [JavaScript runtime 2.0 features for CloudFront Functions](functions-javascript-runtime-20.md).

**Topics**
+ [

## Connection groups
](#connection-groups-helper-function)
+ [

## Distribution tenants
](#distribution-tenants-helper-functions)

## Connection groups
<a name="connection-groups-helper-function"></a>

The connection group that is associated with your distribution tenants has a domain name.

To get this value, use the `endpoint` field of the `context` subobject of the event object. 

**Request**

```
const value = event.context.endpoint;
```

**Response**

The response is a `string` that contains the connection group's domain name, such as d111111abcdef8.cloudfront.net. The `endpoint` field only appears when your function is invoked for a multi-tenant distribution with an associated connection group. For more information, see [Context object](functions-event-structure.md#functions-event-structure-context).

## Distribution tenants
<a name="distribution-tenants-helper-functions"></a>

CloudFront Functions has a module that provides access to specific distribution tenant values.

To use this module, include the following statement in the first line of your function code:

```
import cf from 'cloudfront';
```

You can use the following examples only in the `handler` function, either directly or through any nested-call function.

### `distributionTenant.id` field
<a name="distribution-tenants-field"></a>

Use this field to get the value of distribution tenant ID.

**Request**

```
const value = cf.distributionTenant.id;
```

**Response**

The response is a `string` that contains the distribution tenant ID, such as `dt_1a2b3c4d5e6f7`.

**Error handling**

If your function is invoked for a standard distribution, specifying the `distributionTenant.id` field will return the `distributionTenant module is not available` type error. To handle this use case, you can add a `try` and `catch` block to your code.

### `distributionTenant.parameters.get()` method
<a name="distribution-tenant-parameters-get-method"></a>

Use this method to return the value for the distribution tenant parameter names that you specified.

```
distributionTenant.parameters.get("key");
```

`key`: The distribution tenant parameter name that you want to fetch the value for.

**Request **

```
const value = distributionTenant.parameters.get("key");
```

**Response**

The response is a `string` that contains the value for the distribution tenant parameter. For example, if your key name is `TenantPath`, then the value for this parameter might be `tenant1`.

**Error handling**

You might receive the following errors:
+ If your function is invoked for a standard distribution, the `distributionTenant.parameters.get()` method will return the `distributionTenant module is not available` type error. 
+ The `DistributionTenantParameterKeyNotFound` error is returned when the distribution tenant parameter that you specified doesn't exist. 

To manage these use cases, you can add a `try` and `catch` block to your code.

# Use async and await
<a name="async-await-syntax"></a>

CloudFront Functions JavaScript runtime functions 2.0 provide `async` and `await` syntax to handle `Promise` objects. Promises represent delayed results that can be accessed via the `await` keyword in functions marked as `async`. Various new WebCrypto functions use Promises.

For more information about `Promise` objects, see [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).

**Note**  
You must use JavaScript runtime 2.0 for the following code samples.  
`await` can be used inside `async` functions only. `async` arguments and closures are not supported.

```
async function answer() {
    return 42;
}

// Note: async, await can be used only inside an async function. async arguments and closures are not supported.

async function handler(event) {
    // var answer_value = answer(); // returns Promise, not a 42 value
    let answer_value = await answer(); // resolves Promise, 42
    console.log("Answer"+answer_value);
    event.request.headers['answer'] = { value : ""+answer_value };
    return event.request;
}
```

The following example JavaScript code shows how to view promises with the `then` chain method. You can use `catch` to view errors.

**Warning**  
Using promise combinators (for example, `Promise.all`, `Promise.any`, and promise chain methods (for example, `then` and `catch`) can require high function memory usage. If your function exceeds the [maximum function memory](cloudfront-limits.md#limits-functions) quota, it will fail to execute. To avoid this error, we recommend that you use the `await` syntax instead of `promise` methods.

```
async function answer() {
    return 42;
}

async function squared_answer() {
   return answer().then(value => value * value)
} 
// Note: async, await can be used only inside an async function. async arguments and closures are not supported.
async function handler(event) {
    // var answer_value = answer(); // returns Promise, not a 42 value
    let answer_value = await squared_answer(); // resolves Promise, 42
    console.log("Answer"+answer_value);
    event.request.headers['answer'] = { value : ""+answer_value };
    return event.request;
}
```

# CWT support for CloudFront Functions
<a name="cwt-support-cloudfront-functions"></a>

This section provides details on support for CBOR Web Tokens (CWT) in your CloudFront Functions, which enables secure token-based authentication and authorization at CloudFront Edge Locations. This support is provided as a module, accessible in your CloudFront Function. 

To use this module, create a CloudFront Function using JavaScript runtime 2.0 and include the following statement in the first line of the function code: 

```
import cf from 'cloudfront';
```

The methods associated to this module are accessible through (where \$1 is a wildcard representing the different functions present in the module):

```
cf.cwt.*
```

For more information, see [JavaScript runtime 2.0 features for CloudFront Functions](functions-javascript-runtime-20.md).

Currently, the module only support MAC0 structure with HS256 (HMAC-SHA256) algorithm with a limit of 1KB for the maximum token size.

## Token structure
<a name="token-structure"></a>

This section covers the token structure that is expected by the CWT module. The module expects the token to be correctly tagged and identifiable (e.g. COSE MAC0). Moreover, as for the structure of the token, the module follows the standards set by [CBOR Object Signing and Encryption (COSE) [RFC 8152]](https://datatracker.ietf.org/doc/html/rfc8152).

```
( // CWT Tag (Tag value: 61) --- optional    
    ( // COSE MAC0 Structure Tag (Tag value: 17) --- required        
        [            
            protectedHeaders,            
            unprotectedHeaders,            
            payload,            
            tag,        
        ]    
    )
)
```

**Example : CWT using the COSE MAC0 structure**  

```
61( // CWT tag     
    17( // COSE_MAC0 tag       
        [         
            { // Protected Headers           
                1: 4  // algorithm : HMAC-256-64         
            },         
            { // Unprotected Headers           
                4: h'53796d6d6574726963323536' // kid : Symmetric key id          
            },         
            { // Payload           
                1: "https://iss.example.com", // iss           
                2: "exampleUser", // sub           
                3: "https://aud.example.com", // aud           
                4: 1444064944, // exp           
                5: 1443944944, // nbf           
                6: 1443944944, // iat         
            },         
            h'093101ef6d789200' // tag       
        ]     
    )   
)
```
The CWT tag is optional when generating tokens. However, the COSE structure tag is required.

## validateToken() method
<a name="validatetoken-method"></a>

The function decodes and validates a CWT token using the specified key. If validation is successful, it returns the decoded CWT token. Otherwise, it throws an error. Please note that this function does no validation on the claim set.

### Request
<a name="validatetoken-request"></a>

```
cf.cwt.validateToken(token, handlerContext{key})
```Parameters

**token (required)**  
Encoded token for validation. This must be a JavaScript Buffer.

**handlerContext (Required)**  
A JavaScript Object that stores context for the validateToken call. At present, only the key property is supported.

**key (Required)**  
Secret key for message digest computation. Can be provided either as a string or JavaScript Buffer.

### Response
<a name="validatetoken-response"></a>

When the `validateToken()` method returns a successfully validated token, the response from the function is a `CWTObject` in the following format. Once decoded, all claim keys are represented as strings.

```
CWTObject {    
    protectedHeaders,    
    unprotectedHeaders,    
    payload
}
```

### Example - Validate token with kid sent as part of the token
<a name="validatetoken-example"></a>

This example demonstrates CWT token validation, where the kid is extracted from the header. The kid is then passed into CloudFront Functions KeyValueStore to fetch the secret key used to validate the token.

```
import cf from 'cloudfront'

const CwtClaims = {
   iss: 1,
   aud: 3,
   exp: 4
}

async function handler(event) {
    try {
        let request = event.request;
        let encodedToken = request.headers['x-cwt-token'].value;
        let kid = request.headers['x-cwt-kid'].value;
                
        // Retrieve the secret key from the kvs
        let secretKey = await cf.kvs().get(kid);
                 
        // Now you can use the secretKey to decode & validate the token.
        let tokenBuffer = Buffer.from(encodedToken, 'base64url');
                
        let handlerContext = {
           key: secretKey,
        }
                
        try {
            let cwtObj = cf.cwt.validateToken(tokenBuffer, handlerContext);
                        
            // Check if token is expired
            const currentTime = Math.floor(Date.now() / 1000); // Current time in seconds
            if (cwtObj[CwtClaims.exp] && cwtObj[CwtClaims.exp] < currentTime) {
                return {
                    statusCode: 401,
                    statusDescription: 'Token expired'
                };
            }
        } catch (error) {
            return {
               statusCode: 401,
               statusDescription: 'Invalid token'
            };
         }
    } catch (error) {
        return {
            statusCode: 402,
            statusDescription: 'Token processing failed'
        };
     }
    return request;
}
```

## generateToken() method
<a name="generatetoken-method"></a>

This function generates a new CWT token using the provided payload and context settings.

### Request
<a name="generatetoken-request"></a>

```
cf.cwt.generateToken(generatorContext, payload)
```Parameters

**generatorContext (Required)**  
This a JavaScript Object that is used as the context for generating the token and contains the following key value pairs:    
**cwtTag (Optional)**  
This value is a boolean, which if `true` specifies the `cwtTag` should be added.  
**coseTag (Required)**  
Specifies the COSE tag type. Currently only supports `MAC0`.  
**key (Required)**  
Secret key to compute message digest. This value can be either a string or JavaScript `Buffer`.

**payload (Required)**  
Token payload for encoding. The payload must be in `CWTObject` format.

### Response
<a name="generatetoken-response"></a>

Returns a JavaScript Buffer containing the encoded token.

**Example : Generate a CWT token**  

```
import cf from 'cloudfront';

const CwtClaims = {
    iss: 1,
    sub: 2,
    exp: 4
};

const CatClaims = {
    catu: 401,
    catnip: 402,
    catm: 403,
    catr: 404
};

const Catu = {
    host: 1,
    path: 2,
    ext: 3
};

const CatuMatchTypes = {
    prefix_match: 1,
    suffix_match: 2,
    exact_match: 3
};

const Catr = {
    renewal_method: 1,
    next_renewal_time: 2,
    max_uses: 3
};

async function handler(event) {
    try {
        const response = {
            statusCode: 200,
            statusDescription: 'OK',
            headers: {}
        };
        
        const commonAccessToken = {
            protected: {
                1: "5",
            },
            unprotected: {},
            payload: {
                [CwtClaims.iss]: "cloudfront-documentation",
                [CwtClaims.sub]: "cwt-support-on-cloudfront-functions",
                [CwtClaims.exp]: 1740000000,
                [CatClaims.catu]: {
                    [Catu.host]: {
                        [CatuMatchTypes.suffix_match]: ".cloudfront.net"
                    },
                    [Catu.path]: {
                        [CatuMatchTypes.prefix_match]: "/media/live-stream/cf-4k/"
                    },
                    [Catu.ext]: {
                        [CatuMatchTypes.exact_match]: [
                            ".m3u8",
                            ".ts",
                            ".mpd"
                        ]
                    }
                },
                [CatClaims.catnip]: [
                    "[IP_ADDRESS]",
                    "[IP_ADDRESS]"
                ],
                [CatClaims.catm]: [
                    "GET",
                    "HEAD"
                ],
                [CatClaims.catr]: {
                    [Catr.renewal_method]: "header_renewal",
                    [Catr.next_renewal_time]: 1750000000,
                    [Catr.max_uses]: 5
                }
            }
        };
        
        if (!request.headers['x-cwt-kid']) {
            throw new Error('Missing x-cwt-kid header');
        }
        
        const kid = request.headers['x-cwt-kid'].value;
        const secretKey = await cf.kvs().get(kid);
        
        if (!secretKey) {
            throw new Error('Secret key not found for provided kid');
        }
        
        try {
            const genContext = {
                cwtTag: true,
                coseTag: "MAC0",
                key: secretKey
            };
            
            const tokenBuffer = cf.cwt.generateToken(commonAccessToken, genContext);
            response.headers['x-generated-cwt-token'] = { value: tokenBuffer.toString('base64url') };
                        
            return response;
        } catch (tokenError) {
            return {
                statusCode: 401,
                statusDescription: 'Could not generate the token'
            };
        }
    } catch (error) {
        return {
            statusCode: 402,
            statusDescription: 'Token processing failed'
        };
    }
}
```

**Example : Refresh token based on some logic**  

```
import cf from 'cloudfront'

const CwtClaims = {
   iss: 1,
   aud: 3,
   exp: 4
}

async function handler(event) {
    try {
        let request = event.request;
        let encodedToken = request.headers['x-cwt-token'].value;
        let kid = request.headers['x-cwt-kid'].value;
        let secretKey = await cf.kvs().get(kid); // Retrieve the secret key from the kvs
                
        // Now you can use the secretKey to decode & validate the token.
        let tokenBuffer = Buffer.from(encodedToken, 'base64url');
                
        let handlerContext = {
           key: secretKey,
        }
                
        try {
            let cwtJSON = cf.cwt.validateToken(tokenBuffer, handlerContext);
                        
            // Check if token is expired
            const currentTime = Math.floor(Date.now() / 1000); // Current time in seconds
            if (cwtJSON[CwtClaims.exp] && cwtJSON[CwtClaims.exp] < currentTime) {
                // We can regnerate the token and add 8 hours to the expiry time
                cwtJSON[CwtClaims.exp] = Math.floor(Date.now() / 1000) + (8 * 60 * 60);
                                
                let genContext = {
                  coseTag: "MAC0",
                  key: secretKey
                }
                                
                let newTokenBuffer = cf.cwt.generateToken(cwtJSON, genContext);
                 request.headers['x-cwt-regenerated-token'] = newTokenBuffer.toString('base64url');
            }
        } catch (error) {
            return {
               statusCode: 401,
               statusDescription: 'Invalid token'
            };
         }
    }
    catch (error) {
        return {
            statusCode: 402,
            statusDescription: 'Token processing failed'
        };
     }
    return request;
}
```

# General helper methods
<a name="general-helper-methods"></a>

This page provides additional helper methods inside CloudFront Functions. To use these methods, create a CloudFront function using JavaScript runtime 2.0.

```
import cf from 'cloudfront';
```

For more information, see [JavaScript runtime 2.0 features for CloudFront Functions](functions-javascript-runtime-20.md).

## `edgeLocation` metadata
<a name="edge-location-metadata"></a>

This method requires using the `cloudfront` module.

**Note**  
You can only use this method for viewer-request functions. For viewer-response functions, this method is empty.

Use this JavaScript object to obtain the edge location airport code, expected [Regional Edge Cache](HowCloudFrontWorks.md#CloudFrontRegionaledgecaches) region or the CloudFront server IP address used to handle the request. This metadata is available only the viewer request event trigger.

```
cf.edgeLocation = {
    name: SEA
    serverIp: 1.2.3.4
    region: us-west-2
}
```

The `cf.edgeLocation` object can contain the following:

**name**  
The three-letter [IATA code](https://en.wikipedia.org/wiki/IATA_airport_code) of the edge location that handled the request.

**serverIp**  
The IPv4 or IPv6 address of the server that handled the request.

**region**  
The CloudFront Regional Edge Cache (REC) that the request is *expected* to use if there is a cache miss. This value is not updated in the event that the expected REC is unavailable and a backup REC is used for the request. This doesn't include the Origin Shield location being used, except in cases when the primary REC and the Origin Shield are the same location.

**Note**  
CloudFront Functions isn't invoked a second time when CloudFront is configured to use origin failover. For more information, see [Optimize high availability with CloudFront origin failover](high_availability_origin_failover.md).

## `rawQueryString()` method
<a name="raw-query-string-method"></a>

This method doesn't require the `cloudFront` module.

Use the `rawQueryString()` method to retrieve the unparsed and unaltered query string as a string.

**Request**

```
function handler(event) {
    var request = event.request;
    const qs = request.rawQueryString();
}
```

**Response**

Returns the full query string of the incoming request as a string value without the leading `?`. 
+ If there isn't a query string, but the `?` is present, the functions returns an empty string. 
+ If there isn't a query string and the `?` isn't present, the function returns `undefined`.

**Case 1: Full query string returned (without leading `?`)**  
Incoming request URL: `https://example.com/page?name=John&age=25&city=Boston`  
`rawQueryString()` returns: `"name=John&age=25&city=Boston"`

**Case 2: Empty string returned (when `?` is present but without parameters)**  
Incoming request URL: `https://example.com/page?`  
`rawQueryString()` returns: `""`

**Case 3: `undefined` returned (no query string and no `?`)**  
Incoming request URL: `https://example.com/page`  
`rawQueryString()` returns: `undefined`

# Create functions
<a name="create-function"></a>

You create a function in two stages: 

1. Create the function code as JavaScript. You can use the default example from the CloudFront console or write your own. For more information, see the following topics:
   + [Write function code](writing-function-code.md)
   + [CloudFront Functions event structure](functions-event-structure.md)
   + [CloudFront Functions examples for CloudFront](service_code_examples_cloudfront_functions_examples.md)

1. Use CloudFront to create the function and include your code. The code exists inside the function (not as a reference).

------
#### [ Console ]

**To create a function**

1. Sign in to the CloudFront console at [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) and choose the **Functions** page.

1. Choose **Create function**.

1. Enter a function name that is unique within the AWS account, choose the JavaScript version, and then choose **Continue**. The details page for the new function appears.
**Note**  
To use [key-value pairs](kvs-with-functions.md) in the function, you must choose JavaScript runtime 2.0.

1. In the **Function code** section, choose the **Build ** tab and enter your function code. The sample code that is included in the **Build** tab illustrates the basic syntax for the function code.

1. Choose **Save changes**.

1. If the function code uses key-value pairs, you must associate a key value store. 

   You can associate the key value store when you first create the function. Or, you can associate it later, by [updating the function](update-function.md). 

   To associate a key value store now, follow these steps:
   + Go to the **Associate KeyValueStore** section and choose **Associate existing KeyValueStore**.
   + Select the key value store that contains the key-value pairs in the function, and then choose **Associate KeyValueStore**.

   CloudFront immediately associates the store with the function. You don't need to save the function.

------
#### [ CLI ]

If you use the CLI, you typically first create the function code in a file, and then create the function with the AWS CLI.

**To create a function**

1. Create the function code in a file, and store it in a directory that your computer can connect to. 

1. Run the command as shown in the example. This example uses the `fileb://` notation to pass in the file. It also includes line breaks to make the command more readable. 

   ```
   aws cloudfront create-function \
       --name MaxAge \
       --function-config '{"Comment":"Max Age 2 years","Runtime":"cloudfront-js-2.0","KeyValueStoreAssociations":{"Quantity":1,"Items":[{"KeyValueStoreARN":"arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"}]}}' \
       --function-code fileb://function-max-age-v1.js
   ```
**Notes**  
`Runtime` – The version of JavaScript. To use [key value pairs](kvs-with-functions.md) in the function, you must specify version 2.0.
`KeyValueStoreAssociations` – If your function uses key-value pairs, you can associate the key value store when you first create the function. Or, you can associate it later, by using `update-function`. The `Quantity` is always `1` because each function can have only one key value store associated with it.

   When the command is successful, you see output like the following.

   ```
   ETag: ETVABCEXAMPLE
   FunctionSummary:
     FunctionConfig:
       Comment: Max Age 2 years
       Runtime: cloudfront-js-2.0
       KeyValueStoreAssociations= \
         {Quantity=1, \
         Items=[{KeyValueStoreARN='arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111'}]} \
     FunctionMetadata:
       CreatedTime: '2021-04-18T20:38:56.915000+00:00'
       FunctionARN: arn:aws:cloudfront::111122223333:function/MaxAge
       LastModifiedTime: '2023-11-19T20:38:56.915000+00:00'
       Stage: DEVELOPMENT
     Name: MaxAge
     Status: UNPUBLISHED
   Location: https://cloudfront.amazonaws.com/2020-05-31/function/arn:aws:cloudfront:::function/MaxAge
   ```

   Most of the information is repeated from the request. Other information is added by CloudFront.
**Notes**  
`ETag` – This value changes each time you modify the key value store. You use this value and the function name to reference the function in the future. Make sure that you always use the current `ETag`.
`FunctionARN` – The ARN for your CloudFront function.
111122223333 – The AWS account.
`Stage` – The stage of the function (`LIVE` or `DEVELOPMENT`). 
`Status` – The status of the function (`PUBLISHED` or `UNPUBLISHED`).

------

After you create the function, it's added to the `DEVELOPMENT` stage. We recommend that you [test your function](test-function.md) before you [publish it](publish-function.md). After you publish your function, the function changes to the `LIVE` stage.

# Test functions
<a name="test-function"></a>

Before you deploy the function to the live stage (production), you can test your function to verify that it works as intended. To test a function, you specify an *event object* that represents an HTTP request or response that your CloudFront distribution could receive in production. 

CloudFront Functions does the following:

1. Runs the function, using the provided event object as input.

1. Returns the function's result (the modified event object) along with any function logs or error messages and the function's *compute utilization*. For more information about compute utilization, see [Understand compute utilization](#compute-utilization).

**Note**  
When you test a function, CloudFront only validates against function execution errors. CloudFront doesn't validate whether the request will flow through successfully once published. For example, if your function deletes a required header, the test will succeed because there isn't an issue with the code. However, if you publish the function and associate it with a distribution, the function will fail when a request was made through CloudFront.

**Contents**
+ [

## Set up the event object
](#test-function-create-event)
+ [

## Test the function
](#test-function-step-test)
+ [

## Understand compute utilization
](#compute-utilization)

## Set up the event object
<a name="test-function-create-event"></a>

Before you test a function, you must set up the event object to test it with. There are several options.

**Option 1: Set up an event object without saving it**  
You can set up an event object in the visual editor in the CloudFront console and not save it.   
You can use this event object to test the function from the CloudFront console, even though it's not saved.

**Option 2: Create an event object in the visual editor**  
You can set up an event object in the visual editor in the CloudFront console and not save it. You can create 10 event objects for each function so that you can, for example, test different possible inputs.  
When you create the event object in this way, you can use the event object to test the function in the CloudFront console. You can't use it to test the function using an AWS API or SDK. 

**Option 3: Create an event object using a text editor**  
You can use a text editor to create an event object in JSON format. For information about the structure of an event object, see [Event structure](functions-event-structure.md).   
You can use this event object to test the function using the CLI. But you can't use it to test the function in the CloudFront console.

**To create an event object (option 1 or 2)**

1. Sign in to the CloudFront console at [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) and choose the **Functions** page.

   Choose the function that you want to test.

1. On the function details page, choose the **Test** tab. 

1. For **Event type**, choose one of the following options:
   + Choose **Viewer request** if the function modifies an HTTP request or generates a response based on the request. The **Request** section appears.
   + Choose **Viewer response**. The **Request** and the **Response** sections appear. 

1. Complete the fields to include in the event. You can choose **Edit JSON** to view the raw JSON.

1. (Optional) To save the event, choose **Save** and in the **Save test event**, enter a name and then choose **Save**.

   You can also choose **Edit JSON** and copy the raw JSON, and save it in your own file, outside of CloudFront. 

**To create an event object (option 3)**

Create the event object using a text editor. Store the file in a directory that your computer can connect to. 

Verify that you follow these guidelines:
+ Omit the `distributionDomainName`, `distributionId`, and `requestId` fields. 
+ The names of headers, cookies, and query strings must be lowercase.

One option for creating an event object in this way is to create a sample using the visual editor. You can be sure that the sample is correctly formatted. You can then copy the raw JSON and paste it into a text editor and save the file.

For more information about the structure of an event, see [Event structure](functions-event-structure.md). 

## Test the function
<a name="test-function-step-test"></a>

You can test a function in the CloudFront console or with the AWS Command Line Interface (AWS CLI).

------
#### [ Console ]

**To test the function**

1. Sign in to the CloudFront console at [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) and choose the **Functions** page.

1. Choose the function that you want to test.

1. Choose the **Test** tab. 

1. Make sure that the correct event is displayed. To switch from the currently displayed event, choose another event in the **Select test event** field.

1. Choose **Test function**. The console shows the output of the function, including function logs and compute utilization. 

------
#### [ CLI ]

You can test a function by using the **aws cloudfront test-function** command. 

**To test the function**

1. Open a command line window.

1. Run the following command from the same directory that contains the specified file.

   This example uses the `fileb://` notation to pass in the event object file. It also includes line breaks to make the command more readable. 

   ```
   aws cloudfront test-function \
       --name MaxAge \
       --if-match ETVABCEXAMPLE \
       --event-object fileb://event-maxage-test01.json \
       --stage DEVELOPMENT
   ```
**Notes**  
You reference the function by its name and ETag (in the `if-match` parameter). You reference the event object by its location in your file system.
The stage can be `DEVELOPMENT` or `LIVE`.

   When the command is successful, you see output like the following.

   ```
   TestResult:
     ComputeUtilization: '21'
     FunctionErrorMessage: ''
     FunctionExecutionLogs: []
     FunctionOutput: '{"response":{"headers":{"cloudfront-functions":{"value":"generated-by-CloudFront-Functions"},"location":{"value":"https://aws.amazon.com/cloudfront/"}},"statusDescription":"Found","cookies":{},"statusCode":302}}'
     FunctionSummary:
       FunctionConfig:
         Comment: MaxAge function
         Runtime: cloudfront-js-2.0
         KeyValueStoreAssociations= \
         {Quantity=1, \
         Items=[{KeyValueStoreARN='arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111'}]} \
       FunctionMetadata:
         CreatedTime: '2021-04-18T20:38:56.915000+00:00'
         FunctionARN: arn:aws:cloudfront::111122223333:function/MaxAge
         LastModifiedTime: '2023-17-20T10:38:57.057000+00:00'
         Stage: DEVELOPMENT
       Name: MaxAge
       Status: UNPUBLISHED
   ```

------

**Notes**  
`FunctionExecutionLogs` contains a list of log lines that the function wrote in `console.log()` statements (if any).
`ComputeUtilization` contains information about running your function. See [Understand compute utilization](#compute-utilization).
`FunctionOutput` contains the event object that the function returned. 

## Understand compute utilization
<a name="compute-utilization"></a>

**Compute utilization** is the amount of time that the function took to run as a percentage of the maximum allowed time. For example, a value of 35 means that the function completed in 35% of the maximum allowed time.

If a function continuously exceeds the maximum allowed time, CloudFront throttles the function. The following list explains the likelihood of a function getting throttled based on the compute utilization value.

**Compute utilization value:**
+ **1 – 50** – The function is comfortably below the maximum allowed time and should run without throttling.
+ **51 – 70** – The function is nearing the maximum allowed time. Consider optimizing the function code.
+ **71 – 100** – The function is very close to or exceeds the maximum allowed time. CloudFront is likely to throttle this function if you associate it with a distribution.

# Update functions
<a name="update-function"></a>

You can update a function at any time. The changes are made only to the version of the function that is in the `DEVELOPMENT` stage. To copy the updates from the `DEVELOPMENT` stage to `LIVE`, you must [publish the function](publish-function.md). 

You can update a function's code in the CloudFront console or with the AWS Command Line Interface (AWS CLI).

------
#### [ Console ]

**To update function code**

1. Sign in to the CloudFront console at [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) and choose the **Functions** page.

   Choose the function to update.

1. Choose **Edit** and make the following changes:
   + Update any fields in the **Details** section.
   + Change or remove the associated key value store. For more information about key value stores, see [Amazon CloudFront KeyValueStore](kvs-with-functions.md).
   + Change the function code. Choose the **Build** tab, make changes, then choose **Save changes** to save changes to the code.

------
#### [ CLI ]

**To update the function code**

1. Open a command line window.

1. Run the following command.

   This example uses the `fileb://` notation to pass in the file. It also includes line breaks to make the command more readable. 

   ```
   aws cloudfront update-function \
       --name MaxAge \
       --function-config '{"Comment":"Max Age 2 years","Runtime":"cloudfront-js-2.0","KeyValueStoreAssociations":{"Quantity":1,"Items":[{"KeyValueStoreARN":"arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"}]}}' \
       --function-code fileb://function-max-age-v1.js \
       --if-match ETVABCEXAMPLE
   ```
**Notes**  
You can identify the function by both its name and ETag (in the `if-match` parameter). Make sure that you use the current ETag. You can get this value from the [DescribeFunction](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_DescribeFunction.html) API operation.
You must include the `function-code`, even if you don't want to change it.
Be careful with the `function-config`. You should pass everything that you want to keep in the configuration. Specifically, handle the key value store as follows:   
To retain the existing key value store association (if there is one), specify the name of the *existing *store.
To change the association, specify the name of the *new *key value store.
To remove the association, omit the `KeyValueStoreAssociations` parameter. 

   When the command is successful, you see output like the following. 

   ```
   ETag: ETVXYZEXAMPLE
   FunctionSummary:
     FunctionConfig:
       Comment: Max Age 2 years \
       Runtime: cloudfront-js-2.0 \
       KeyValueStoreAssociations= \
         {Quantity=1, \
         Items=[{KeyValueStoreARN='arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111'}]} \
     FunctionMetadata: \
       CreatedTime: '2021-04-18T20:38:56.915000+00:00' \
       FunctionARN: arn:aws:cloudfront::111122223333:function/MaxAge \
       LastModifiedTime: '2023-12-19T23:41:15.389000+00:00' \
       Stage: DEVELOPMENT \
     Name: MaxAge \
     Status: UNPUBLISHED
   ```

------

Most of the information is repeated from the request. Other information is added by CloudFront.

**Notes**  
`ETag` – This value changes each time you modify the key value store.
`FunctionARN` – The ARN for your CloudFront function.
`Stage` – The stage for the function (`LIVE` or `DEVELOPMENT`). 
`Status` – The status of the function (`PUBLISHED` or `UNPUBLISHED`).

# Publish functions
<a name="publish-function"></a>

When you publish your function, this copies the function from the `DEVELOPMENT` stage to the `LIVE` stage.

If cache behaviors aren't associated with the function, publishing it enables you to associate it with a cache behavior. You can only associate cache behaviors with functions that are in the `LIVE` stage.

**Important**  
Before you publish, we recommend that you [test the function](test-function.md).
After you publish the function, all cache behaviors that are associated with that function automatically start using the newly published copy, as soon as the distributions finish deploying.

You can publish a function in the CloudFront console or with the AWS CLI.

------
#### [ Console ]

**To publish a function**

1. Sign in to the CloudFront console at [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) and choose the **Functions** page.

1. Choose the function to update.

1. Choose the **Publish** tab and then choose **Publish**. If your function is already attached to one or more cache behaviors, choose **Publish and update**.

1. (Optional) To see the distributions that are associated with the function, choose **Associated CloudFront distributions** to expand that section.

When successful, a banner appears at the top of the page that says ***Function name* published successfully**. You can also choose the **Build** tab and then choose **Live** to see the live version of the function code.

------
#### [ CLI ]

**To publish a function**

1. Open a command line window.

1. Run the following **aws cloudfront publish-function** command. In the example, line breaks are provided to make the example more readable.

   ```
   aws cloudfront publish-function \
       --name MaxAge \
       --if-match ETVXYZEXAMPLE
   ```

   When the command is successful, you see output like the following.

   ```
   FunctionSummary:
     FunctionConfig:
       Comment: Max Age 2 years
       Runtime: cloudfront-js-2.0
     FunctionMetadata:
       CreatedTime: '2021-04-18T21:24:21.314000+00:00'
       FunctionARN: arn:aws:cloudfront::111122223333:function/ExampleFunction
       LastModifiedTime: '2023-12-19T23:41:15.389000+00:00'
       Stage: LIVE
     Name: MaxAge
     Status: UNASSOCIATED
   ```

------

# Associate functions with distributions
<a name="associate-function"></a>

To use a function with a distribution, you associate the function with one or more cache behaviors in the distribution. You can associate a function with multiple cache behaviors in multiple distributions.

You can associate a function with any of the following:
+ An existing cache behavior
+ A new cache behavior in an existing distribution
+ A new cache behavior in a new distribution

When you associate a function with a cache behavior, you must choose an *event type*. The event type determines when CloudFront runs the function. 

You can choose the following event types:
+ **Viewer request** – The function runs when CloudFront receives a request from a viewer.
+ **Viewer response** – The function runs before CloudFront returns a response to the viewer.

You can't use origin-facing event types (*origin request* and *origin response*) with CloudFront Functions. Instead you can use Lambda@Edge. For more information, see [CloudFront events that can trigger a Lambda@Edge function](lambda-cloudfront-trigger-events.md). 

**Note**  
Before you associate a function, you must [publish it](publish-function.md) to the `LIVE` stage.

You can associate a function with a distribution in the CloudFront console or with the AWS Command Line Interface (AWS CLI). The following procedure shows how to associate a function with an existing cache behavior. 

------
#### [ Console ]

**To associate a function with an existing cache behavior**

1. Sign in to the CloudFront console at [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) and choose the **Functions** page.

1. Choose the function that you want to associate.

1. On the **Function** page, choose the **Publish** tab.

1. Choose **Publish function**.

1. Choose **Add association**. On the dialog box that appears, choose a distribution, an event type, and/or a cache behavior. 

   For the event type, choose when you want this function to run:
   + **Viewer Request** – Run the function every time CloudFront receives a request.
   + **Viewer Response** – Run the function every time CloudFront returns a response.

1. To save the configuration, choose **Add association**.

CloudFront associates the distribution with the function. Wait a few minutes for the associated distribution to finish deploying. You can choose **View distribution** on the function details page to check the progress.

------
#### [ CLI ]

**To associate a function with an existing cache behavior**

1. Open a command line window.

1. Enter the following command to save the distribution configuration for the distribution whose cache behavior you want to associate with a function. This command saves the distribution configuration to a file named `dist-config.yaml`. To use this command, do the following:
   + Replace *`DistributionID`* with the distribution's ID.
   + Run the command on one line. In the example, line breaks are provided to make the example more readable.

   ```
   aws cloudfront get-distribution-config \
       --id DistributionID \
       --output yaml > dist-config.yaml
   ```

   When the command is successful, the AWS CLI returns no output.

1. Open the file named `dist-config.yaml` that you created. Edit the file to make the following changes.

   1. Rename the `ETag` field to `IfMatch`, but don't change the field's value.

   1. In the cache behavior, find the object named `FunctionAssociations`. Update this object to add a function association. The YAML syntax for a function association looks like the following example.
      + The following example shows a viewer request event type (trigger). To use a viewer response event type, replace `viewer-request` with `viewer-response`.
      + Replace *`arn:aws:cloudfront::111122223333:function/ExampleFunction`* with the Amazon Resource Name (ARN) of the function that you're associating with this cache behavior. To get the function ARN, you can use the **aws cloudfront list-functions** command.

      ```
      FunctionAssociations:
        Items:
          - EventType: viewer-request
            FunctionARN: arn:aws:cloudfront::111122223333:function/ExampleFunction
        Quantity: 1
      ```

   1. After making these changes, save the file.

1. Use the following command to update the distribution, adding the function association. To use this command, do the following:
   + Replace *`DistributionID`* with the distribution's ID.
   + Run the command on one line. In the example, line breaks are provided to make the example more readable.

   ```
   aws cloudfront update-distribution \
       --id DistributionID \
       --cli-input-yaml file://dist-config.yaml
   ```

   When the command is successful, you see output like the following that describes the distribution that was just updated with the function association. The following example output is truncated for readability.

   ```
   Distribution:
     ARN: arn:aws:cloudfront::111122223333:distribution/EBEDLT3BGRBBW
     ... truncated ...
     DistributionConfig:
       ... truncated ...
       DefaultCacheBehavior:
         ... truncated ...
         FunctionAssociations:
           Items:
           - EventType: viewer-request
             FunctionARN: arn:aws:cloudfront::111122223333:function/ExampleFunction
           Quantity: 1
         ... truncated ...
     DomainName: d111111abcdef8.cloudfront.net
     Id: EDFDVBD6EXAMPLE
     LastModifiedTime: '2021-04-19T22:39:09.158000+00:00'
     Status: InProgress
   ETag: E2VJGGQEG1JT8S
   ```

------

The distribution's `Status` changes to `InProgress` while the distribution is redeployed. When the new distribution configuration reaches a CloudFront edge location, that edge location starts using the associated function. When the distribution is fully deployed, the `Status` changes back to `Deployed`. This indicates that the associated CloudFront function is live in all CloudFront edge locations worldwide. This typically takes a few minutes.

# Amazon CloudFront KeyValueStore
<a name="kvs-with-functions"></a>

CloudFront KeyValueStore is a secure, global, low-latency key value datastore that allows read access from within [CloudFront Functions](cloudfront-functions.md), enabling advanced customizable logic at the CloudFront edge locations. 

With CloudFront KeyValueStore, you make updates to function code and updates to the data associated with a function independently of each other. This separation simplifies function code and makes it easy to update data without the need to deploy code changes. 

**Note**  
To use CloudFront KeyValueStore, your CloudFront function must use [JavaScript runtime 2.0](functions-javascript-runtime-20.md).

The following is the general procedure for using key-value pairs: 
+ Create key value stores, and populate it with a set of key-value pairs. You can add your key value stores to an Amazon S3 bucket or enter them manually.
+ Associate the key value stores with your CloudFront function.
+ Within your function code, use the name of the key to either retrieve the value associated with the key or to evaluate if a key exists. For more information about using key-value pairs in function code, and about helper methods, see [Helper methods for key value stores](functions-custom-methods.md).

## Use cases
<a name="key-value-store-use-cases"></a>

You can use key-value pairs for the following examples:
+ **URL rewrites or redirects **– The key-value pair can hold the rewritten URLs or the redirect URLs.
+ **A/B testing and feature flags **– You can create a function to run experiments by assigning a percentage of traffic to a specific version of your website. 
+ **Access authorization** – You can implement access control to allow or deny requests based on criteria defined by you and the data stored in a key value store.

## Supported formats for values
<a name="key-value-store-supported-formats"></a>

You can store the value in a key-value pair in any of the following formats:
+ String
+ Byte-encoded string
+ JSON 

## Security
<a name="key-value-store-security"></a>

The CloudFront function and all its key value stores data are handled securely, as follows:
+ CloudFront encrypts each key value stores at rest and during transit (when reading or writing to the key value stores) when you call the [CloudFront KeyValueStore](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_Operations_Amazon_CloudFront_KeyValueStore.html) API operations.
+ When the function is run, CloudFront decrypts each key-value pair in memory at the CloudFront edge locations. 

To get started with CloudFront KeyValueStore, see the following topics. 

**Topics**
+ [

## Use cases
](#key-value-store-use-cases)
+ [

## Supported formats for values
](#key-value-store-supported-formats)
+ [

## Security
](#key-value-store-security)
+ [

# Work with key value store
](kvs-with-functions-kvs.md)
+ [

# Work with key value data
](kvs-with-functions-kvp.md)
+ For more information about getting started with CloudFront KeyValueStore, see the [Introducing Amazon CloudFront KeyValueStore](https://aws.amazon.com/blogs/aws/introducing-amazon-cloudfront-keyvaluestore-a-low-latency-datastore-for-cloudfront-functions/) AWS blog post.

# Work with key value store
<a name="kvs-with-functions-kvs"></a>

You must create a key value store to hold the key-value pairs that you want to use in CloudFront Functions. 

After you create the key value stores and added key-value pairs, you can use the key values in your CloudFront function code. 

To get started, see the following topics: 

**Topics**
+ [

# Create a key value store
](kvs-with-functions-create.md)
+ [

# Associate a key value store with a function
](kvs-with-functions-associate.md)
+ [

# Update a key value store
](kvs-with-functions-edit.md)
+ [

# Get a reference to a key value store
](kvs-with-functions-get-reference.md)
+ [

# Delete a key value store
](kvs-with-functions-delete.md)
+ [

# File format for key-value pairs
](kvs-with-functions-create-s3-kvp.md)

**Note**  
The JavaScript runtime 2.0 includes some helper methods for working with key values in the function code. For more information, see [Helper methods for key value stores](functions-custom-methods.md).

# Create a key value store
<a name="kvs-with-functions-create"></a>



You can create a key value store and its key-value pairs at the same time. You can also create an empty key value store now and then add the key-value pairs later. 

**Note**  
If you specify your data source from an Amazon S3 bucket, you must have the `s3:GetObject` and `s3:GetBucketLocation` permissions to that bucket. If you don't have these permissions, CloudFront can't successfully create your key value store.

Decide if you want to add key-value pairs at the same time when you create the key value store. You can import your key-value pairs by using the CloudFront console, CloudFront API, or AWS SDKs. However, you can only import your file of key-value pairs when you *initially* create the key value store. 

To create a file of key-value pairs, see [File format for key-value pairs](kvs-with-functions-create-s3-kvp.md). 

------
#### [ Console ]

**To create a key value store**

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

1. Choose the **KeyValueStores** tab, and then choose **Create KeyValueStore**.

1. Enter a name and optional description for the key value store. 

1. Complete **S3 URI**: 
   + If you have a file of key-value pairs, enter the path to the Amazon S3 bucket where you stored the file. 
   + Leave this field blank if you plan to enter the key-value pairs manually. 

1. Choose **Create**. The key value store now exists.

   The details page for the new key value store appears. The information on the page includes the ID and the ARN of the key value store. 
   + The ID is a random string of characters that is unique in your AWS account. 
   + The ARN has this syntax:

     *AWS account*`:key-value-store/`*the key value stores ID*

1. Look at the **Key value pairs** section. If you imported a file, this section shows some key-value pairs. You can do the following:
   + If you imported a file, you can also add more values manually. 
   + If you didn't import a file from an Amazon S3 bucket, and if you want to add key-value pairs now, you can complete the next step.
   + You can skip this step and add the key-value pairs later. 

1. To add the pairs now:

   1. Choose **Add key-value pairs**. 

   1. Choose **Add pair** and enter a name and value. Repeat this step to add more pairs.

   1. When you're finished, choose **Save changes** to save all the key-value pairs in the key value store. On the dialog box that appears, choose **Done**.

1. To associate the key value store with a function now, complete the **Associated functions** section. For more information, see [Create functions](create-function.md) or [Update functions](update-function.md). 

   You can also associate the function later, either from this key value store details page, or from the function's details page.

------
#### [ AWS CLI ]

**To create a key value store**
+ Run the following command to create a key value store and import the key-value pairs from an Amazon S3 bucket.

  ```
  aws cloudfront create-key-value-store \
      --name=keyvaluestore1 \
      --comment="This is my key value store file" \
      --import-source=SourceType=S3,SourceARN=arn:aws:s3:::amzn-s3-demo-bucket1/kvs-input.json
  ```

  **Response**

  ```
  {
      "ETag": "ETVABCEXAMPLE",
      "Location": "https://cloudfront.amazonaws.com/2020-05-31/key-value-store/arn:aws:cloudfront::123456789012:key-value-store/8aa76c93-3198-462c-aaf6-example",
      "KeyValueStore": {
          "Name": "keyvaluestore1",
          "Id": "8aa76c93-3198-462c-aaf6-example",
          "Comment": "This is my key value store file",
          "ARN": "arn:aws:cloudfront::123456789012:key-value-store/8aa76c93-3198-462c-aaf6-example",
          "Status": "PROVISIONING",
          "LastModifiedTime": "2024-08-06T22:19:10.813000+00:00"
      }
  }
  ```

------
#### [ API ]

**To create a key value store**

1. Use the [CloudFront CreateKeyValueStore](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_CreateKeyValueStore.html) operation. The operation takes several parameters:
   + A `name` of the key value store.
   + A `comment` parameter that includes a comment.
   + An `import-source` parameter that lets you import key-value pairs from a file that is stored in an Amazon S3 bucket. You can import from a file only when you first create the key value store. For information about the file structure, see [File format for key-value pairs](kvs-with-functions-create-s3-kvp.md).

The operation response includes the following information:
+ The values passed in the request, including the name that you assigned.
+ Data such as the creation time.
+ An `ETag` (for example, `ETVABCEXAMPLE`), the ARN that includes the name of the key value store (for example, `arn:aws:cloudfront::123456789012:key-value-store/keyvaluestore1`). 

  You will use some combination of the `ETag`, the ARN, and the name to work with the key value store programmatically.

------

## Key value store statuses
<a name="key-value-store-status"></a>

When you create a key value store, the data store can have the following status values.


****  

| Value | Description | 
| --- | --- | 
|  **Provisioning**  |  The key value store was created and CloudFront is processing the data source that you specified.  | 
|  **Ready**  |  The key value store was created and CloudFront successfully processed the data source that you specified.  | 
|  **Import failed**  |  CloudFront wasn't able to process the data source that you specified. This status can appear if your file format isn't valid or that it exceeds the size limit. For more information, see [File format for key-value pairs](kvs-with-functions-create-s3-kvp.md).  | 

# Associate a key value store with a function
<a name="kvs-with-functions-associate"></a>

After you create your key value store, you can update your function to associate it with your key value store. You must make this association to use the key-value pairs from that store in that function. The following rules apply:
+ A function can have only one key value store
+ You can associate the same key value store with multiple functions

------
#### [ Console ]

**To associate a key value store with a function**

1. Sign in to the CloudFront console at [https://console.aws.amazon.com/cloudfront/v4/home#/functions](https://console.aws.amazon.com/cloudfront/v4/home#/functions) and choose the **Functions** page.

1. Choose the function name.

1. Go to the **Associate KeyValueStore** section and choose **Associate existing KeyValueStore**.

1. Select the key value store that contains the key-value pairs in the function, and then choose **Associate KeyValueStore**.

   CloudFront immediately associates the store with the function. You don't need to save the function.

1. To specify a different key value store, choose **Update associated KeyValueStore**, select another key value store name, and then choose **Associate KeyValueStore**.

For more information, see [Update functions](update-function.md).

------
#### [ AWS CLI ]

**To associate a key value store with a function**
+ Run the following command to update the `MaxAge` function and associate a key value store resource.

  ```
  aws cloudfront update-function \
      --name MaxAge \
      --function-config '{"Comment":"Max Age 2 years","Runtime":"cloudfront-js-2.0","KeyValueStoreAssociations":{"Quantity":1,"Items":[{"KeyValueStoreARN":"arn:aws:cloudfront::123456789012:key-value-store/8aa76c93-3198-462c-aaf6-example"}]}}' \
      --function-code fileb://function-max-age-v1.js \
      --if-match ETVABCEXAMPLE
  ```
+ To associate a key value store with a function, specify the `KeyValueStoreAssociations` parameter and the key value store ARN. 
+ To change the association, specify another key value store ARN. 
+ To remove the association, remove the `KeyValueStoreAssociations` parameter. 

For more information, see [Update functions](update-function.md).

------
#### [ API ]

**To associate a key value store with a function**
+ Use the [UpdateFunction](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateFunction.html) API operation. For more information, see [Update functions](update-function.md).

------

**Notes**  
If you modify a key value store without changing the key-value pairs, or if you only modify the key-value pairs in the key value store, you don't need to associate the key value store again. You also don't need to republish the function.  
However, we recommend that you test the function to verify that it works as expected. For more information, see [Test functions](test-function.md).
You can view all the functions that use specific key value stores. On the CloudFront console, choose the key value store details page. 

# Update a key value store
<a name="kvs-with-functions-edit"></a>

When you update a key value store, you can change the key-value pairs, or change the association between the key value store and the function.

------
#### [ Console ]

**To update a key value store**

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

1. Choose the **KeyValueStores** tab.

1.  Select the key value store that you want to update. 
   + To update the key-value pairs, choose **Edit** in the **Key value pairs** section. You can add or delete any key-value pairs. You can also change the value for an existing key-value pair. When you're finished, choose **Save changes**.
   + To update the association for this key value store, choose **Go to functions**. For more information, see [Associate a key value store with a function](kvs-with-functions-associate.md).

------
#### [ AWS CLI ]

**To update a key value store**

1. **Change the key-value pairs** – You can add more key-value pairs, delete one or more key-value pairs, and change the value of an existing key-value pair. For more information, see [Work with key value data](kvs-with-functions-kvp.md).

1. **Change the function association for the key value store ** – To update the function the association for the key value store, see [Associate a key value store with a function](kvs-with-functions-associate.md). 
**Tip**  
You will need the ARN of the key value store. For more information, see [Get a reference to a key value store](kvs-with-functions-get-reference.md).

------
#### [ API ]

**To update a key value store**

1. **Change the key-value pairs** – You can add more key-value pairs, delete one or more key-value pairs, and change the value of an existing key-value pair. For more information, see [Work with key value data](kvs-with-functions-kvp.md).

1. **Change the function association for the key value store** – To update the function association for the key value store, use the [UpdateFunction](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_UpdateFunction.html) API operation. For more information, see [Update functions](update-function.md). 
**Tip**  
You will need the ARN of the key value store. For more information, see [Get a reference to a key value store](kvs-with-functions-get-reference.md).

------

# Get a reference to a key value store
<a name="kvs-with-functions-get-reference"></a>

To work with the key value stores programmatically, you need the `ETag` and the name of the key value store. 

To get both values, you can use the AWS Command Line Interface (AWS CLI) or the CloudFront API.

------
#### [ AWS CLI ]

**To get the key value store reference**

1. To return a list of key value stores, run the following command Find the name of the key value store that you want to change.

   ```
   aws cloudfront list-key-value-stores
   ```

1. From the response, find the name of the key value store that you want.

   **Response**

   ```
   {
       "KeyValueStoreList": {
           "Items": [
               {
                   "Name": "keyvaluestore3",
                   "Id": "37435e19-c205-4271-9e5c-example3",
                   "ARN": "arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example3",
                   "Status": "READY",
                   "LastModifiedTime": "2024-05-08T14:50:18.876000+00:00"
               },
               {
                   "Name": "keyvaluestore2",
                   "Id": "47970d59-6408-474d-b850-example2",
                   "ARN": "arn:aws:cloudfront::123456789012:key-value-store/47970d59-6408-474d-b850-example2",
                   "Status": "READY",
                   "LastModifiedTime": "2024-05-30T21:06:22.113000+00:00"
               },
               {
                   "Name": "keyvaluestore1",
                   "Id": "8aa76c93-3198-462c-aaf6-example",
                   "ARN": "arn:aws:cloudfront::123456789012:key-value-store/8aa76c93-3198-462c-aaf6-example",
                   "Status": "READY",
                   "LastModifiedTime": "2024-08-06T22:19:30.510000+00:00"
               }
           ]
       }
   }
   ```

1. Run the following command to return the `ETag` for the specified key value store.

   ```
   aws cloudfront describe-key-value-store \
       --name=keyvaluestore1
   ```

   **Response**

   ```
   {
       "ETag": "E3UN6WX5RRO2AG",
       "KeyValueStore": {
           "Name": "keyvaluestore1",
           "Id": "8aa76c93-3198-462c-aaf6-example",
           "Comment": "This is an example KVS",
           "ARN": "arn:aws:cloudfront::123456789012:key-value-store/8aa76c93-3198-462c-aaf6-example",
           "Status": "READY",
           "LastModifiedTime": "2024-08-06T22:19:30.510000+00:00"
       }
   }
   ```

------
#### [ API ]

**To get the key value store reference**

1. Use the [https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_ListKeyValueStores.html](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_ListKeyValueStores.html) API operation to return a list of key value stores. Find the name of the key value store that you want to change. 

1. Use the [https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_DescribeKeyValueStore.html](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_DescribeKeyValueStore.html) API operation and specify the name of the key value store that you returned from the previous step. 

------

The response includes a UUID, the ARN of the key value store, and the `ETag` of the key value store.
+ An `ETag`, such as `E3UN6WX5RRO2AG`
+ The UUID is 128 bits, such as `8aa76c93-3198-462c-aaf6-example`
+ The ARN includes the AWS account number, the constant `key-value-store`, and the UUID, like the following example:

  `arn:aws:cloudfront::123456789012:key-value-store/8aa76c93-3198-462c-aaf6-example`

For more information about the `DescribeKeyValueStore` operation, see [About the CloudFront KeyValueStore](kvs-with-functions-kvp.md#kvs-with-functions-api-describe).

# Delete a key value store
<a name="kvs-with-functions-delete"></a>

You can delete your key value store by using the Amazon CloudFront console or API.

------
#### [ Console ]

**To delete a key value store**

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

1. Choose the function name.

1. Under the **Associated KeyValueStore** section, verify if a key value store is associated with the function. If it is, remove the association by choosing **Disassociate KeyValueStore** and then choose **Remove association**.

1. In the navigation pane, choose the **Functions** page and then choose the **KeyValueStores** tab. 

1. Select the key value store that you want to delete and then choose **Delete**.

------
#### [ AWS CLI ]

**To delete a key value store**

1. Get the `ETag` and the name of the key value stores. For more information, see [Get a reference to a key value store](kvs-with-functions-get-reference.md).

1. Verify if the key value stores is associated with a function. If it is, remove the association. For more information on both these steps, see [Update functions](update-function.md).

1. After you have the name and `ETag` of the key value store and it's no longer associated with a function, you can delete it.

   Run the following command to delete the specified key value store.

   ```
   aws cloudfront delete-key-value-store \
       --name=keyvaluestore1 \
       --if-match=E3UN6WX5RRO2AG
   ```

------
#### [ API ]

**To delete a key value store**

1. Get the `ETag` and the name of the key value stores. For more information, see [Get a reference to a key value store](kvs-with-functions-get-reference.md).

1. Verify if the key value stores is associated with a function. If it is, remove the association. For more information on both these steps, see [Update functions](update-function.md).

1. To delete the key value store, use the CloudFront [https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_DeleteKeyValueStore.html](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_DeleteKeyValueStore.html) API operation.

------

# File format for key-value pairs
<a name="kvs-with-functions-create-s3-kvp"></a>

When you create a UTF-8 encoded file, use the following JSON format:

```
{
  "data":[
    {
      "key":"key1",
      "value":"value"
    },
    {
      "key":"key2",
      "value":"value"
    }
  ]
}
```

Your file can't include duplicate keys. If you specified an invalid file in your Amazon S3 bucket, you can update the file to remove any duplicates and then try creating your key value store again.

For more information, see [Create a key value store](kvs-with-functions-create.md).

**Note**  
The file for your data source and its key-value pairs have the following limits:  
File size – 5 MB
Key size – 512 characters
Value size – 1024 characters

# Work with key value data
<a name="kvs-with-functions-kvp"></a>

This topic describes how to add key-value pairs to an existing key value store. To include key-value pairs when you initially create the key value stores, see [Create a key value store](kvs-with-functions-create.md).

**Topics**
+ [

## Work with key-value pairs (console)
](#kvs-with-functions-kvp-using-console)
+ [

## About the CloudFront KeyValueStore
](#kvs-with-functions-api-describe)
+ [

## Work with key-value pairs (AWS CLI)
](#work-with-kvs-cli-keys)
+ [

## Work with key-value pairs (API)
](#kvs-with-functions-kvp-using-api)

## Work with key-value pairs (console)
<a name="kvs-with-functions-kvp-using-console"></a>

You can use the CloudFront console to work with your key-value pairs.

**To work with key-value pairs**

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

1. Choose the **KeyValueStores** tab. 

1. Select the key value store that you want to change.

1. In the **Key value pairs** section, choose **Edit**. 

1. You can add a key-value pair, delete a key-value pair, or change the value for an existing key-value pair. 

1. When you're finished, choose **Save changes**.

## About the CloudFront KeyValueStore
<a name="kvs-with-functions-api-describe"></a>

**Tip**  
The CloudFront KeyValueStore API is a global service that uses Signature Version 4A (SigV4A) for authentication. Using temporary credentials with SigV4A requires version 2 session tokens. For more information, see [Using temporary credentials with the CloudFront KeyValueStore API](cloudfront-function-restrictions.md#regional-endpoint-for-key-value-store).

If you're using the AWS Command Line Interface (AWS CLI) or your own code to call the CloudFront KeyValueStore API, see the following sections. 

When you work with a key value store and its key-value pairs, the service that you call depends on your use case:
+ To work with key-value pairs in an *existing* key value store, use the CloudFront KeyValueStore service. 
+ To include key-value pairs in the key value store when you *initially* create the key value store, use the CloudFront service.

Both the CloudFront API and the CloudFront KeyValueStore API have a `DescribeKeyValueStore` operation. You call them for different reasons. To understand the differences, see the following table.


|  | CloudFront DescribeKeyValueStore API | CloudFront KeyValueStore DescribeKeyValueStore API | 
| --- | --- | --- | 
| Data about the key value store |  Returns data, such as the status and the date that the key value store itself was last modified.  |  Returns data about the *contents* of the storage resource – the key-value pairs in the store, and the size of the contents.  | 
| Data that identifies the key value store |  Returns an `ETag`, the UUID, and the ARN of the key value store.  |  Returns an `ETag` and the ARN of the key value store.  | 

**Notes**  
Each DescribeKeyValueStore operation returns a *different * `ETag`. The `ETags` aren't interchangeable.
When you call an API operation to complete an action, you must specify the `ETag` from the appropriate API. For example, in the CloudFront KeyValueStore [ DeleteKey](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_DeleteKey.html) operation, you specify the `ETag` that you returned from the CloudFront KeyValueStore [https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_DescribeKeyValueStore.html](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_DescribeKeyValueStore.html) operation.
When you invoke your CloudFront Functions by using CloudFront KeyValueStore, the values in the key value store aren't updated or changed during the invocation of the function. Updates are processed in between invocations of a function.

## Work with key-value pairs (AWS CLI)
<a name="work-with-kvs-cli-keys"></a>

You can run the following AWS Command Line Interface commands for CloudFront KeyValueStore.

**Contents**
+ [

### List key-value pairs
](#kvs-cli-list-keys)
+ [

### Get key-value pairs
](#kvs-cli-get-keys)
+ [

### Describe a key value store
](#kvs-cli-describe-keys)
+ [

### Create a key-value pair
](#kvs-cli-create-keys)
+ [

### Delete a key-value pair
](#kvs-cli-delete-keys)
+ [

### Update key-value pairs
](#kvs-cli-update-key)

### List key-value pairs
<a name="kvs-cli-list-keys"></a>

To list key-value pairs in your key value store, run the following command.

```
aws cloudfront-keyvaluestore list-keys \
    --kvs-arn=arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example
```

**Response**

```
{
    "Items": [
        {
            "Key": "key1",
            "Value": "value1"
        }
    ]
}
```

### Get key-value pairs
<a name="kvs-cli-get-keys"></a>

To get a key-value pair in your key value store, run the following command.

```
aws cloudfront-keyvaluestore get-key \
    --key=key1 \
    --kvs-arn=arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example
```

**Response**

```
{
    "Key": "key1",
    "Value": "value1",
    "ItemCount": 1,
    "TotalSizeInBytes": 11
}
```

### Describe a key value store
<a name="kvs-cli-describe-keys"></a>

To describe a key value store, run the following command.

```
aws cloudfront-keyvaluestore describe-key-value-store \
    --kvs-arn=arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example
```

**Response**

```
{
    "ETag": "KV1F83G8C2ARO7P",
    "ItemCount": 1,
    "TotalSizeInBytes": 11,
    "KvsARN": "arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example",
    "Created": "2024-05-08T07:48:45.381000-07:00",
    "LastModified": "2024-08-05T13:50:58.843000-07:00",
    "Status": "READY"
}
```

### Create a key-value pair
<a name="kvs-cli-create-keys"></a>

To create a key-value pair in your key value store, run the following command.

```
aws cloudfront-keyvaluestore put-key \
    --if-match=KV1PA6795UKMFR9 \
    --key=key2 \
    --value=value2 \
    --kvs-arn=arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example
```

**Response**

```
{
    "ETag": "KV13V1IB3VIYZZH",
    "ItemCount": 3,
    "TotalSizeInBytes": 31
}
```

### Delete a key-value pair
<a name="kvs-cli-delete-keys"></a>

To delete a key-value pair, run the following command.

```
aws cloudfront-keyvaluestore delete-key \
    --if-match=KV13V1IB3VIYZZH \
    --key=key1 \
    --kvs-arn=arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example
```

**Output**

```
{
    "ETag": "KV1VC38T7YXB528",
    "ItemCount": 2,
    "TotalSizeInBytes": 22
}
```

### Update key-value pairs
<a name="kvs-cli-update-key"></a>

You can use the `update-keys` command to update more than one key-value pair. For example, to delete an existing key-value pair and create another one, run the following command.

```
aws cloudfront-keyvaluestore update-keys \
    --if-match=KV2EUQ1WTGCTBG2 \
    --kvs-arn=arn:aws:cloudfront::123456789012:key-value-store/37435e19-c205-4271-9e5c-example \
    --deletes '[{"Key":"key2"}]' \
    --puts '[{"Key":"key3","Value":"value3"}]'
```

**Response**

```
{
    "ETag": "KV3AEGXETSR30VB",
    "ItemCount": 3,
    "TotalSizeInBytes": 28
}
```

## Work with key-value pairs (API)
<a name="kvs-with-functions-kvp-using-api"></a>

Follow this section to work with your key-value pairs programatically. 

**Contents**
+ [

### Get a reference to a key value store
](#kvs-with-functions-api-ref)
+ [

### Change key-value pairs in a key value store
](#kvs-with-functions-api-actions)
+ [

### Example code for CloudFront KeyValueStore
](#example-code-key-value-store)

### Get a reference to a key value store
<a name="kvs-with-functions-api-ref"></a>

When you use the CloudFront KeyValueStore API to call a write operation, you need to specify the ARN and the `ETag` of the key value store. To get this data, do the following:

**To get a reference to a key value store**

1. Use the [https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_ListKeyValueStores.html](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_ListKeyValueStores.html) API operation to get a list of key value stores. Find the key value store that you want to change. 

1. Use the [CloudFrontKeyValueStore DescribeKeyValueStore API operation](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_DescribeKeyValueStore.html) and specify the key value store from the previous step.

   The response includes the ARN and the `ETag` of the key value store. 
   + The ARN includes the AWS account number, the constant `key-value-store`, and the UUID, such as the following example:

     `arn:aws:cloudfront::123456789012:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111`
   + An `ETag` that looks like the following example: 

     `ETVABCEXAMPLE2`

### Change key-value pairs in a key value store
<a name="kvs-with-functions-api-actions"></a>

You can specify the key value store that contains the key-value pair that you want to update. 

See the following CloudFront KeyValueStore API operations:
+ [CloudFrontKeyValueStore DeleteKey](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_DeleteKey.html) – Deletes a key-value pair
+ [CloudFrontKeyValueStore GetKey](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_GetKey.html) – Returns a key-value pair
+ [CloudFrontKeyValueStore ListKeys](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_ListKeys.html) – Returns a list of key-value pairs 
+ [CloudFrontKeyValueStore PutKey](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_PutKey.html) – You can perform the following tasks:
  + Create a key-value pair in one key value store by specifying a new key name and value.
  + Set a different value in an existing key-value pair by specifying an existing key name, and a new key value.
+ [CloudFrontKeyValueStore UpdateKeys](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_kvs_UpdateKeys.html) – You can perform one or more of the following actions in one all-or-nothing operation:
  + Delete one or more key-value pairs
  + Create one or more new key-value pairs
  + Set a different value in one or more existing key-value pairs

### Example code for CloudFront KeyValueStore
<a name="example-code-key-value-store"></a>

**Example**  
The following code shows you how to call the `DescribeKeyValueStore` API operation for a key value store.  

```
const {
  CloudFrontKeyValueStoreClient,
  DescribeKeyValueStoreCommand,
} = require("@aws-sdk/client-cloudfront-keyvaluestore");

require("@aws-sdk/signature-v4-crt");

(async () => {
  try {
    const client = new CloudFrontKeyValueStoreClient({
      region: "us-east-1"
    });
    const input = {
      KvsARN: "arn:aws:cloudfront::123456789012:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111",
    };
    const command = new DescribeKeyValueStoreCommand(input);

    const response = await client.send(command);
  } catch (e) {
    console.log(e);
  }
})();
```

# Customize with CloudFront Connection Functions
<a name="customize-connections-validation-with-connection-functions"></a>

CloudFront Connection Functions allow you to write lightweight JavaScript functions for mTLS certificate validation and custom authentication logic. Your Connection Functions run during mTLS connection establishment to validate client certificates, implement device-specific authentication rules, and handle certificate revocation scenarios. The Connection Functions runtime environment offers submillisecond startup times, scales immediately to handle millions of connections per second, and is highly secure. Connection Functions are a native feature of CloudFront, which means you can build, test, and deploy your code entirely within CloudFront.

When you associate a Connection Function with an mTLS-enabled CloudFront distribution, CloudFront intercepts TLS connection requests at CloudFront edge locations and passes certificate information to your function. You can invoke Connection Functions when the following event occurs:
+ During TLS connection establishment (connection request) - for mutual TLS (mTLS) connections

For more information about Connection Functions, see the following topics.

**Topics**
+ [

# Overview and workflow
](connection-functions-overview.md)
+ [

# Configuration and limits
](connection-function-configuration-limits.md)
+ [

# Create CloudFront Connection Functions for mutual TLS (viewer) validation
](create-connection-functions.md)
+ [

# Write CloudFront Connection Function code for mutual TLS (viewer) validation
](write-connection-function-code.md)
+ [

# Test CloudFront Connection Functions before deployment
](test-connection-functions.md)
+ [

# Associate Connection Functions with distributions
](associate-connection-functions.md)
+ [

# Implement certificate revocation for mutual TLS (viewer) with CloudFront Functions and KeyValueStore
](implement-certificate-revocation.md)

# Overview and workflow
<a name="connection-functions-overview"></a>

CloudFront Connection Functions are a specialized type of CloudFront Functions that run during the TLS handshake when a client attempts to establish an mTLS connection. Your Connection Function can access client certificate information, mTLS configuration parameters, certificate revocation check results, and the client IP address.

Connection functions are invoked after CloudFront performs standard certificate validation (trust chain, expiry, signature verification) but can run even if certificate revocation checks fail. This allows you to implement custom logic for handling revoked certificates or adding additional validation criteria.

After you create and publish a Connection Function, make sure to add an association for the connection request event type with an mTLS-enabled distribution. This makes the function run each time a client attempts to establish an mTLS connection with CloudFront.

CloudFront Connection Functions follow a two-stage lifecycle that allows you to develop and test functions before deploying them to production. This workflow ensures that your Connection Functions work correctly before they affect live traffic.

**Topics**
+ [

## Function stages
](#connection-function-stages)
+ [

## Development workflow
](#connection-function-development-workflow)
+ [

## Differences from other function types
](#connection-function-differences)

## Function stages
<a name="connection-function-stages"></a>

Connection functions exist in one of two stages:
+ **DEVELOPMENT** – Functions in this stage can be modified, tested, and updated. Use this stage for writing and debugging your function code.
+ **LIVE** – Functions in this stage are read-only and handle production traffic. You cannot modify functions in the LIVE stage directly.

When you create a new Connection Function, it starts in the **DEVELOPMENT** stage. After testing and validation, you publish the function to move it to the **LIVE** stage.

## Development workflow
<a name="connection-function-development-workflow"></a>

Follow this workflow to develop and deploy Connection Functions:

1. **Create** – Create a new Connection Function in the DEVELOPMENT stage with your initial code and configuration.

1. **Test** – Use the test functionality to validate your function with sample connection events before deployment.

1. **Update** – Modify the function code and configuration as needed based on test results.

1. **Publish** – When ready for production, publish the function to move it from DEVELOPMENT to LIVE stage.

1. **Associate** – Associate the published function with your mTLS-enabled distribution to handle live connections.

To make changes to a LIVE function, you must update the DEVELOPMENT version and publish it again. This creates a new version in the LIVE stage.

## Differences from other function types
<a name="connection-function-differences"></a>

Connection functions differ from viewer request and viewer response functions in several important ways:
+ Connection functions run after the mTLS handshake, before any HTTP processing occurs
+ Connection functions have access to TLS certificate information instead of HTTP request/response data
+ Connection functions can only allow or deny the connection, not modify HTTP data
+ Connection functions are only invoked for new TLS connections, not for connection reuse
+ TLS session resumption is not supported with mTLS to ensure certificate validation occurs on every connection
+ Connection functions run in addition to standard viewer request and viewer response functions
+ You associate Connection Functions at the distribution level, instead of at the cache behavior level.
+ Connection functions only support JavaScript runtime 2.0.

# Configuration and limits
<a name="connection-function-configuration-limits"></a>

CloudFront Connection Functions have specific configuration requirements and service limits due to their specialized role in TLS connection validation and the performance requirements of edge computing.

**Topics**
+ [

## Function code requirements
](#connection-function-code-requirements)
+ [

## Service limits
](#connection-function-service-limits)
+ [

## Function filtering options
](#connection-function-filtering-options)

## Function code requirements
<a name="connection-function-code-requirements"></a>

Connection functions require JavaScript code that processes TLS connection events. The function code must:
+ Be written in JavaScript
+ Process connection events and make allow/deny decisions
+ Complete execution within the time limits
+ Handle certificate and connection validation logic

## Service limits
<a name="connection-function-service-limits"></a>

Connection functions are subject to the following limits:
+ **Function size** – Function code and configuration are limited in size
+ **Execution time** – Functions have strict execution time limits for TLS connection processing
+ **Association limits** – Each distribution can have only one Connection Function associated
+ **Stage restrictions** – Only LIVE stage functions can be associated with distributions

## Function filtering options
<a name="connection-function-filtering-options"></a>

When listing Connection Functions, you can use the following filters:
+ **Stage filter** – Filter by DEVELOPMENT or LIVE stage
+ **Association filter** – Filter by distribution ID or key-value store ID associations

These filters help you organize and manage Connection Functions across different environments and use cases.

# Create CloudFront Connection Functions for mutual TLS (viewer) validation
<a name="create-connection-functions"></a>

You create a CloudFront Connection Function in two stages:

1. Create the function code as JavaScript. You can use the default example from the CloudFront console or write your own. For more information, see the following topics:
   + Write CloudFront Connection Function code for mTLS validation
   + CloudFront Connection Function event structure and response format
   + Connection function code examples

1. Use CloudFront to create the Connection Function and include your code. The code exists inside the function (not as a reference).

**Topics**
+ [

## CloudFront console
](#create-connection-function-console)
+ [

## AWS CLI
](#create-connection-function-cli)

## CloudFront console
<a name="create-connection-function-console"></a>

**To create a Connection Function**

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 **Create function**.

1. Enter a function name that is unique within the AWS account, choose **Connection function** as the function type, and then choose **Continue**.

1. The details page for the new Connection Function appears.
**Note**  
Connection functions only support JavaScript runtime 2.0. To use CloudFront Connection Function KeyValueStore integration in the function, you must use this runtime version.

1. In the **Function code** section, choose the **Build** tab and enter your connection function code. The sample code that is included in the Build tab illustrates the basic syntax for Connection Function code.

1. Choose **Save changes**.

1. If the Connection Function code uses KeyValueStore for certificate revocation checking or device validation, you must associate a KeyValueStore.

   You can associate the KeyValueStore when you first create the function. Or, you can associate it later, by associating Connection Functions.

   To associate a KeyValueStore now, follow these steps:
   + Go to the **Associate KeyValueStore** section and choose **Associate existing KeyValueStore**.
   + Select the KeyValueStore that contains the certificate data for your Connection Function, and then choose **Associate KeyValueStore**.

   CloudFront immediately associates the store with the function. You don't need to save the function.

## AWS CLI
<a name="create-connection-function-cli"></a>

If you use the AWS CLI, you typically first create the Connection Function code in a file, and then create the function with the AWS CLI.

**To create a Connection Function**

1. Create the Connection Function code in a file, and store it in a directory that your computer can connect to.

1. Run the command as shown in the example. This example uses the `fileb://` notation to pass in the file. It also includes line breaks to make the command more readable.

   ```
   aws cloudfront create-connection-function \
       --name CertificateValidator \
       --connection-function-config '{
           "Comment":"Device certificate validation",
           "Runtime":"cloudfront-js-2.0",
           "KeyValueStoreAssociations":{
               "Quantity":1,
               "Items":[{
                   "KeyValueStoreARN":"arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"
               }]
           }
       }' \
       --connection-function-code fileb://certificate-validator.js
   ```
**Note**  
**Runtime** – Connection functions only support JavaScript runtime 2.0 (cloudfront-js-2.0).
**KeyValueStoreAssociations** – If your Connection Function uses KeyValueStore for certificate validation, you can associate the KeyValueStore when you first create the function. Or, you can associate it later, by using update-connection-function. The Quantity is always 1 because each Connection Function can have only one KeyValueStore associated with it.

1. When the command is successful, you see output like the following.

   ```
   ETag: ETVABCEXAMPLE
   ConnectionFunctionSummary:
     ConnectionFunctionConfig:
       Comment: Device certificate validation
       Runtime: cloudfront-js-2.0
       KeyValueStoreAssociations:
         Quantity: 1
         Items:
           - KeyValueStoreARN: arn:aws:cloudfront::111122223333:key-value-store/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111
     ConnectionFunctionMetadata:
       CreatedTime: '2024-09-04T16:32:54.292000+00:00'
       ConnectionFunctionARN: arn:aws:cloudfront::111122223333:connection-function/CertificateValidator
       LastModifiedTime: '2024-09-04T16:32:54.292000+00:00'
       Stage: DEVELOPMENT
     Name: CertificateValidator
     Status: UNPUBLISHED
   Location: https://cloudfront.amazonaws.com/2020-05-31/connection-function/arn:aws:cloudfront:::connection-function/CertificateValidator
   ```

   Most of the information is repeated from the request. Other information is added by CloudFront.
**Note**  
**ETag** – This value changes each time you modify the Connection Function. You need this value to update or publish the function.
**Stage** – New connection functions start in the DEVELOPMENT stage. You must publish the function to move it to the LIVE stage before associating it with a distribution.
**Status** – The function status is UNPUBLISHED until you publish it to the LIVE stage.

# Write CloudFront Connection Function code for mutual TLS (viewer) validation
<a name="write-connection-function-code"></a>

CloudFront Connection Functions enable you to write lightweight JavaScript functions for mTLS certificate validation and custom authentication logic. Your Connection Function code can validate client certificates, implement device-specific authentication rules, handle certificate revocation scenarios, and make allow/deny decisions for TLS connections at CloudFront edge locations worldwide.

Connection functions provide a powerful way to extend CloudFront's built-in certificate validation with your own business logic. Unlike viewer request and viewer response functions that process HTTP data, Connection Functions operate at the TLS layer and have access to certificate information, client IP addresses, and TLS connection details. This makes them ideal for implementing zero-trust security models, device authentication systems, and custom certificate validation policies that go beyond standard PKI validation.

Your Connection Function code runs in a secure, isolated environment with submillisecond startup times and can scale to handle millions of connections per second. The runtime is optimized for certificate validation workloads and provides built-in integration with CloudFront KeyValueStore for real-time data lookup operations, enabling sophisticated authentication scenarios like certificate revocation list checking and device allowlist validation.

To help you write effective Connection Function code, see the following topics. For complete code examples and step-by-step tutorials, see the tutorial sections in this guide and explore the Connection Function examples available in the CloudFront console.

**Topics**
+ [

## CloudFront Connection Function use cases and purposes
](#connection-function-use-cases)
+ [

## CloudFront Connection Function event structure and response format
](#connection-function-event-structure)
+ [

## CloudFront Connection Functions JavaScript runtime features
](#connection-function-javascript-runtime)
+ [

## CloudFront Connection Function helper methods and APIs
](#connection-function-helper-methods)
+ [

## CloudFront Connection Function KeyValueStore integration
](#connection-function-kvs-integration)
+ [

## Use async and await
](#connection-function-async-await)
+ [

## Connection function code examples
](#connection-function-code-examples)

## CloudFront Connection Function use cases and purposes
<a name="connection-function-use-cases"></a>

Before writing your CloudFront Connection Function, carefully determine what type of certificate validation or authentication logic you need to implement. Connection functions are designed for specific use cases that require custom validation beyond standard PKI certificate checking. Understanding your use case helps you design efficient code that meets your security requirements while maintaining optimal performance.

Common Connection Function use cases include:
+ **Certificate revocation handling** – Implement custom policies for handling revoked certificates, including grace periods for certificate rotation, trusted network exceptions for internal devices, or emergency access scenarios where revoked certificates might need temporary access.
+ **Optional mTLS support** – Handle both mTLS and non-mTLS connections with different authentication policies, allowing you to provide enhanced security for clients that support certificates while maintaining compatibility with legacy clients.
+ **IP-based authentication** – Combine certificate validation with client IP address checks for enhanced security, such as restricting access from specific geographic regions, corporate networks, or known malicious IP ranges.
+ **Multi-tenant certificate validation** – Implement tenant-specific validation rules where different certificate authorities or validation criteria apply based on the client certificate issuer or subject attributes.
+ **Time-based access control** – Enforce time-based restrictions where certificates are only valid during specific hours, maintenance windows, or business periods, even if the certificate itself hasn't expired.

Connection functions run after CloudFront performs standard certificate validation (trust chain verification, expiry checks, and signature validation) but before the TLS connection is established. This timing gives you the flexibility to add custom validation criteria while benefiting from CloudFront's built-in certificate validation. Your function receives the results of standard validation and can make informed decisions about whether to allow or deny the connection based on both standard and custom criteria.

When designing your Connection Function, consider the performance implications of your validation logic. Functions have a 5-millisecond execution limit, so complex operations should be optimized for speed. Use KeyValueStore for fast data lookups rather than complex calculations, and structure your validation logic to fail fast for invalid certificates.

## CloudFront Connection Function event structure and response format
<a name="connection-function-event-structure"></a>

CloudFront Connection Functions receive a different event structure than viewer request and viewer response functions. Instead of HTTP request/response data, connection functions receive certificate and connection information that you can use to make authentication decisions.

**Topics**
+ [

### Event structure for Connection Functions
](#connection-function-event-structure-details)
+ [

### Connection Functions response format
](#connection-function-response-format)

### Event structure for Connection Functions
<a name="connection-function-event-structure-details"></a>

Connection Functions receive an event object that contains certificate and connection information. The event structure of the function is shown below:

```
{
  "clientCertificate": {
    "certificates": {
      "leaf": {
        "serialNumber": "string",
        "issuer": "string",
        "subject": "string",
        "validity": {
          "notBefore": "string",
          "notAfter": "string",
        },
        "sha256Fingerprint": "string"
      }
    }
  },
  "clientIp": "string",
  "endpoint": "string",
  "distributionId": "string",
  "connectionId": "string"
}
```

Below is an example of the event object structure:

```
{
  "clientCertificate": {
    "certificates": {
      "leaf": {
        "serialNumber": "00:9e:2a:af:16:56:e5:47:25:7d:2e:38:c3:f9:9d:57:fa",
        "issuer": "C=US, O=Ram, OU=Edge, ST=WA, CN=mTLS-CA, L=Snoqualmie",
        "subject": "C=US, O=Ram, OU=Edge, ST=WA, CN=mTLS-CA, L=Snoqualmie",
        "validity": {
          "notBefore": "2025-09-10T23:43:10Z",
          "notAfter": "2055-09-11T00:43:02Z"
        },
        "sha256Fingerprint": "_w6bJ7aOAlGOj7NUhJxTfsfee-ONg_xop3_PTgTJpqs="
      }
    }
  },
  "clientIp": "127.0.0.1",
  "endpoint": "d3lch071jze0cb.cloudfront.net",
  "distributionId": "E1NXS4MQZH501R",
  "connectionId": "NpvTe1925xfj24a67sPQr7ae42BIq03FGhJJKfrQYWZcWZFp96SIIg=="
}
```

### Connection Functions response format
<a name="connection-function-response-format"></a>

Your Connection Function must return a response object that indicates whether to allow or deny the connection. Use the helper methods to make connection decisions:

```
function connectionHandler(connection) {
    // Helper methods to allow or deny connections
    if (/* some logic to determine if function should allow connection */) {
        connection.allow();
    } else {
        connection.deny();
    }
}
```

Unlike viewer request and viewer response functions, Connection Functions cannot modify HTTP requests or responses. They can only allow or deny the TLS connection.

## CloudFront Connection Functions JavaScript runtime features
<a name="connection-function-javascript-runtime"></a>

CloudFront Connection Functions use the CloudFront Functions JavaScript runtime 2.0, which provides a secure and high-performance environment specifically optimized for certificate validation workloads. The runtime is designed to start in sub-milliseconds and handle millions of concurrent executions across CloudFront's global edge network.

The runtime environment includes comprehensive JavaScript language support:
+ **ECMAScript 2020 (ES11) support** – Modern JavaScript features including optional chaining (?.), nullish coalescing (??), and BigInt for handling large certificate serial numbers
+ **Built-in objects** – Standard JavaScript objects like Object, Array, JSON, Math, and Date
+ **Console logging** – Use console.log() for debugging and monitoring certificate validation decisions. Logs are available in real-time during testing and can help troubleshoot validation logic in development
+ **KeyValueStore integration** – Native access to CloudFront KeyValueStore for ultra-fast data lookup operations, enabling real-time certificate revocation checking, device allowlist validation, and tenant-specific configuration retrieval

Connection functions are optimized for high-performance for certificate validation scenarios. The runtime automatically handles memory management, garbage collection, and resource cleanup to ensure consistent performance across millions of concurrent connections. All operations are designed to be deterministic and fast, with KeyValueStore lookups typically completing in microseconds.

The runtime environment is completely isolated between function executions, ensuring that no data leaks between different client connections. Each function execution starts with a clean state and has no access to previous execution results or client data from other connections.

## CloudFront Connection Function helper methods and APIs
<a name="connection-function-helper-methods"></a>

CloudFront Connection Functions provide specialized helper methods designed to simplify certificate validation decisions and enhance observability. These methods are optimized for the connection validation workflow and integrate seamlessly with CloudFront's connection logging and monitoring systems.
+ **connection.allow()** – Allow the TLS connection to proceed. This method signals CloudFront to complete the TLS handshake and allow the client to establish the connection. Use this when certificate validation passes and any custom authentication logic is satisfied
+ **connection.deny()** – Deny the TLS connection and terminate the handshake. This method immediately closes the connection and prevents any HTTP traffic from flowing. The client will receive a TLS connection error. Use this for invalid certificates, failed authentication, or policy violations
+ **connection.logCustomData()** – Add custom data to connection logs (up to 800 bytes of UTF-8 text). This method allows you to include validation results, certificate details, or decision rationale in CloudFront connection logs for security monitoring, compliance auditing, and troubleshooting

These methods provide a clean, declarative interface for making connection decisions and logging relevant information for monitoring and debugging. The allow/deny pattern ensures that your function's intent is clear and that CloudFront can optimize connection handling based on your decision. Custom logging data is immediately available in CloudFront connection logs and can be used with log analysis tools for security monitoring and operational insights.

Always call either connection.allow() or connection.deny() before your function completes. If neither method is called, CloudFront will deny the connection by default as a security precaution.

## CloudFront Connection Function KeyValueStore integration
<a name="connection-function-kvs-integration"></a>

CloudFront Connection Functions can use CloudFront KeyValueStore to perform ultra-fast data lookups for certificate validation scenarios. KeyValueStore is particularly powerful for Connection Functions because it provides global, eventually-consistent data access with microsecond lookup times across all CloudFront edge locations. This makes it ideal for maintaining certificate revocation lists, device allowlists, tenant configurations, and other validation data that needs to be accessible during TLS handshakes.

KeyValueStore integration is designed specifically for high-performance connection validation workflows:
+ **kvsHandle.exists(key)** – Check if a key exists in the KeyValueStore without retrieving the value. This is the most efficient method for binary validation scenarios like certificate revocation checking, where you only need to know if a certificate serial number is in a revocation list
+ **kvsHandle.get(key)** – Retrieve a value from the KeyValueStore for more complex validation scenarios. Use this when you need to access configuration data, validation rules, or metadata associated with a certificate or device identifier

KeyValueStore operations are asynchronous and must be used with async/await syntax. The KeyValueStore has a 10MB total size limit and supports up to 10 million key-value pairs. KeyValueStore data is eventually consistent across all edge locations, with updates typically propagating within seconds.

For optimal performance, structure your KeyValueStore keys to minimize lookup operations. Use certificate serial numbers as keys for simple revocation checking, or create composite keys combining issuer hash and serial number for multi-CA environments. Consider the trade-offs between key complexity and KeyValueStore capacity when designing your data structure.

## Use async and await
<a name="connection-function-async-await"></a>

Connection functions support asynchronous operations using async/await syntax, which is essential when working with KeyValueStore operations or other asynchronous tasks. The async/await pattern ensures that your function waits for KeyValueStore lookups to complete before making connection decisions, while maintaining the high-performance characteristics required for TLS handshake processing.

Proper async/await usage is critical for Connection Functions because KeyValueStore operations, while very fast, are still network operations that require coordination across CloudFront's distributed infrastructure. The runtime automatically handles promise resolution and ensures that your function completes within the 5-millisecond execution limit.

**Example : Async Connection Function with KeyValueStore**  

```
import cf from 'cloudfront';

async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    
    // Async operation to check KeyValueStore for certificate revocation
    const isRevoked = await kvsHandle.exists(connection.clientCertificate.certificates.leaf.serialNumber);
    
    if (isRevoked) {
        // Log the revocation decision with certificate details
        connection.logCustomData(`REVOKED_CERT:${connection.clientCertificate.certificates.leaf.serialNumber}:${connection.clientCertificate.certificates.leaf.issuer}`);
        console.log(`Denying connection for revoked certificate: ${connection.clientCertificate.certificates.leaf.serialNumber}`);
        return connection.deny();
    }
    
    // Log successful validation for monitoring
    connection.logCustomData(`VALID_CERT:${connection.clientCertificate.certificates.leaf.serialNumber}`);
    console.log(`Allowing connection for valid certificate: ${connection.clientCertificate.certificates.leaf.serialNumber}`);
    return connection.allow();
}
```

Always use async/await when calling KeyValueStore methods or other asynchronous operations. The Connection Function runtime handles promise resolution automatically and ensures proper execution flow within the strict timing constraints of TLS handshake processing. Avoid using .then() or callback patterns, as async/await provides cleaner error handling and better performance in the Connection Function environment.

When designing async Connection Functions, structure your code to minimize the number of KeyValueStore operations and perform them as early as possible in your validation logic. This ensures maximum performance and reduces the risk of timeout issues during high-traffic periods. Consider batching related validation checks and using the most efficient KeyValueStore method (exists() vs get()) for your use case.

## Connection function code examples
<a name="connection-function-code-examples"></a>

The following examples demonstrate common Connection Function patterns for different validation scenarios. Use these examples as starting points for your own Connection Function implementations.

**Example : Device certificate validation**  
This example validates device serial numbers and certificate subject fields for IoT devices, gaming consoles, and other client authentication scenarios:  

```
async function connectionHandler(connection) {
    // Custom validation: check device serial number format
    const serialNumber = connection.clientCertificate.certificates.leaf.serialNumber;
    if (!serialNumber.startsWith("DEV")) {
        connection.logCustomData(`INVALID_SERIAL:${serialNumber}`);
        return connection.deny();
    }
    
    // Validate certificate subject contains required organizational unit
    const subject = connection.clientCertificate.certificates.leaf.subject;
    if (!subject.includes("OU=AuthorizedDevices")) {
        connection.logCustomData(`INVALID_OU:${subject}`);
        return connection.deny();
    }
    
    // Allow connection for valid devices
    connection.logCustomData(`VALID_DEVICE:${serialNumber}`);
    return connection.allow();
}
```
This function performs multiple validation checks beyond standard certificate validation, including device serial number format and organizational unit verification.

**Example : Optional mTLS with mixed authentication**  
This example handles both mTLS and non-mTLS connections with different authentication policies:  

```
async function connectionHandler(connection) {
    if (connection.clientCertificate) {
        // mTLS connection - enhanced validation for certificate holders
        const subject = connection.clientCertificate.certificates.leaf.subject;
        connection.logCustomData(`MTLS_SUCCESS:${subject}:${connection.clientIp}`);
        console.log(`mTLS connection from: ${subject}`);
        return connection.allow();
    } else {
        // Non-mTLS connection - apply IP-based restrictions
        const clientIp = connection.clientIp;
        
        // Only allow non-mTLS from specific IP ranges
        if (clientIp.startsWith("203.0.113.") || clientIp.startsWith("198.51.100.")) {
            connection.logCustomData(`NON_MTLS_ALLOWED:${clientIp}`);
            console.log(`Non-mTLS connection allowed from: ${clientIp}`);
            return connection.allow();
        }
        
        connection.logCustomData(`NON_MTLS_DENIED:${clientIp}`);
        return connection.deny();
    }
}
```
This function provides enhanced security for clients with certificates while maintaining compatibility with legacy clients from trusted IP ranges.

# Test CloudFront Connection Functions before deployment
<a name="test-connection-functions"></a>

You can test CloudFront Connection Functions in the DEVELOPMENT stage using the TestConnectionFunction API operation. Testing allows you to validate your function logic with sample connection events before publishing to the LIVE stage.

**Topics**
+ [

## Testing process
](#connection-function-testing-process)
+ [

## Test results
](#connection-function-test-results)
+ [

## Connection test object
](#connection-test-object)

## Testing process
<a name="connection-function-testing-process"></a>

To test a Connection Function:

1. Create a Connection Function in the DEVELOPMENT stage

1. Prepare a test connection object that represents the TLS connection event

1. Use the TestConnectionFunction API operation to execute your function with the test data

1. Review the test results, including function output, execution logs, and any error messages

1. Update your function code as needed and repeat the testing process

## Test results
<a name="connection-function-test-results"></a>

When you test a Connection Function, the results include:
+ **Function summary** – Metadata about the function that was tested
+ **Compute utilization** – Performance metrics showing resource usage
+ **Execution logs** – Console output from your function, including any logging statements
+ **Function output** – The result returned by your function
+ **Error messages** – Any runtime errors or exceptions that occurred during execution

## Connection test object
<a name="connection-test-object"></a>

The connection test object is a binary blob (up to 40KB) that represents the TLS connection event your function will process. This object contains the certificate and connection information that your function uses to make authentication decisions.

**Note**  
The specific structure and format of the connection test object is defined by the CloudFront Connection Functions runtime. Consult the CloudFront Functions documentation or contact AWS Support for details on creating appropriate test objects for your use case.

After creating your Connection Function, you can:
+ **Test the function** – Use the test functionality in the console or CLI to validate your function with sample connection events. For more information, see Connection Function testing.
+ **Update the function** – Modify the function code and configuration as needed. Connection functions in the DEVELOPMENT stage can be updated at any time.
+ **Publish the function** – When ready for production, publish the function to move it from DEVELOPMENT to LIVE stage. For more information, see associating Connection Functions.
+ **Associate with a distribution** – Associate the published function with an mTLS-enabled distribution to handle live connections. For more information, see associating connection functions.

# Associate Connection Functions with distributions
<a name="associate-connection-functions"></a>

After publishing a Connection Function to the LIVE stage, you must associate it with an mTLS-enabled distribution to handle live connections. Connection functions are associated at the distribution level, unlike viewer request and viewer response functions which are associated with cache behaviors.

**Topics**
+ [

## Association requirements
](#connection-function-association-requirements)
+ [

## Organizing functions with filters
](#connection-function-organizing-filters)
+ [

## Deployment considerations
](#connection-function-deployment-considerations)

## Association requirements
<a name="connection-function-association-requirements"></a>

To associate a Connection Function with a distribution:
+ The function must be in the LIVE stage
+ The distribution must have mTLS enabled
+ The distribution must have a valid trust store configured
+ You can only associate one Connection Function per distribution

## Organizing functions with filters
<a name="connection-function-organizing-filters"></a>

CloudFront provides filtering capabilities to help you organize and manage connection functions:
+ **Distribution ID filter** – Find functions associated with specific distributions
+ **Key-value store filter** – Find functions that use specific key-value stores for data lookup
+ **Stage filter** – List functions in DEVELOPMENT or LIVE stage

Use these filters when managing multiple Connection Functions across different distributions or development environments.

## Deployment considerations
<a name="connection-function-deployment-considerations"></a>

Consider these factors when deploying Connection Functions:
+ **Global deployment** – Connection functions deploy to all CloudFront edge locations worldwide, which may take several minutes
+ **Version management** – Each published version creates a new LIVE function that replaces the previous version
+ **Rollback strategy** – Plan for rollback by keeping previous working versions of your function code
+ **Testing in production** – Consider using separate distributions for staging and production environments

# Implement certificate revocation for mutual TLS (viewer) with CloudFront Functions and KeyValueStore
<a name="implement-certificate-revocation"></a>

You can use CloudFront Connection Functions with KeyValueStore to implement certificate revocation checking. This lets you maintain a list of revoked certificate serial numbers and check client certificates against this list during the TLS handshake.

To implement certificate revocation, you need these components:
+ A distribution configured with viewer mTLS
+ A KeyValueStore containing revoked certificate serial numbers
+ A Connection Function that queries the KeyValueStore to check certificate status

When a client connects, CloudFront validates the certificate against the trust store, then runs your Connection Function. Your function checks the certificate serial number against the KeyValueStore and allows or denies the connection.

**Topics**
+ [

# Step 1: Create a KeyValueStore for revoked certificates
](create-kvs-revoked-certificates.md)
+ [

# Step 2: Create the revocation Connection Function
](create-revocation-connection-function.md)
+ [

# Step 3: Test your revocation function
](test-revocation-function.md)
+ [

# Step 4: Associate the function to your distribution
](associate-function-distribution.md)
+ [

# Advanced revocation scenarios
](advanced-revocation-scenarios.md)

# Step 1: Create a KeyValueStore for revoked certificates
<a name="create-kvs-revoked-certificates"></a>

Create a KeyValueStore to store revoked certificate serial numbers that your Connection Function can check during mTLS connections.

First, prepare your revoked certificate serial numbers in JSON format:

```
{
  "data": [
    {
      "key": "ABC123DEF456",
      "value": ""
    },
    {
      "key": "789XYZ012GHI", 
      "value": ""
    }
  ]
}
```

Upload this JSON file to an S3 bucket, then create the KeyValueStore:

```
aws s3 cp revoked-serials.json s3://your-bucket-name/revoked-serials.json
aws cloudfront create-key-value-store \
  --name revoked-serials-kvs \
  --import-source '{
    "SourceType": "S3",
    "SourceARN": "arn:aws:s3:::your-bucket-name/revoked-serials.json"
  }'
```

Wait for the KeyValueStore to finish provisioning. Check the status with:

```
aws cloudfront get-key-value-store --name "revoked-serials-kvs"
```

# Step 2: Create the revocation Connection Function
<a name="create-revocation-connection-function"></a>

Create a Connection Function that checks certificate serial numbers against the KeyValueStore to determine if certificates are revoked.

Create a Connection Function that checks certificate serial numbers against the KeyValueStore:

```
aws cloudfront create-connection-function \
  --name "revocation-control" \
  --connection-function-config file://connection-function-config.json \
  --connection-function-code file://connection-function-code.txt
```

The configuration file specifies the KeyValueStore association:

```
{
  "Runtime": "cloudfront-js-2.0",
  "Comment": "A function that implements revocation control via KVS",
  "KeyValueStoreAssociations": {
    "Quantity": 1,
    "Items": [
      {
        "KeyValueStoreArn": "arn:aws:cloudfront::account-id:key-value-store/kvs-id"
      }
    ]
  }
}
```

The Connection Function code checks the KeyValueStore for revoked certificates:

```
import cf from 'cloudfront';

async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    
    // Get parsed client serial number from client certificate
    const clientSerialNumber = connection.clientCertInfo.serialNumber;
    
    // Check KVS to see if serial number exists as a key
    const serialNumberExistsInKvs = await kvsHandle.exists(clientSerialNumber);
    
    // Deny connection if serial number exists in KVS
    if (serialNumberExistsInKvs) {
        console.log("Connection denied - certificate revoked");
        return connection.deny();
    }
    
    // Allow connections that don't exist in kvs
    console.log("Connection allowed");
    return connection.allow();
}
```

# Step 3: Test your revocation function
<a name="test-revocation-function"></a>

Use the CloudFront console to test your Connection Function with sample certificates. Navigate to the Connection Function in the console and use the Test tab.

**Test with sample certificates**

1. Paste a sample certificate in PEM format into the test interface

1. Optionally specify a client IP address for testing IP-based logic

1. Choose **Test function** to see the execution results

1. Review the execution logs to verify your function logic

Test with both valid and revoked certificates to ensure your function handles both scenarios correctly. The execution logs show console.log output and any errors that occur during function execution.

# Step 4: Associate the function to your distribution
<a name="associate-function-distribution"></a>

Once you publish your Connection Function, associate it with your mTLS-enabled distribution to activate certificate revocation checking.

You can associate the function from the distribution settings page or from the Connection Function's associated distributions table. Navigate to your distribution settings, scroll to the **Viewer mutual authentication (mTLS)** section, select your Connection Function, and save the changes.

# Advanced revocation scenarios
<a name="advanced-revocation-scenarios"></a>

For more complex certificate revocation requirements, consider these additional configurations:

**Topics**
+ [

## Convert Certificate Revocation Lists (CRL) to KeyValueStore format
](#convert-crl-kvs-format)
+ [

## Handle multiple Certificate Authorities
](#handle-multiple-cas)
+ [

## Add custom data to connection logs
](#add-custom-data-logs)
+ [

## Manage CRL updates
](#manage-crl-updates)
+ [

## Plan KeyValueStore capacity
](#plan-kvs-capacity)

## Convert Certificate Revocation Lists (CRL) to KeyValueStore format
<a name="convert-crl-kvs-format"></a>

If you have a Certificate Revocation List (CRL) file, you can convert it to KeyValueStore JSON format using OpenSSL and jq:

**Convert CRL to KeyValueStore format**

Extract serial numbers from the CRL file:

```
openssl crl -text -noout -in rfc5280_CRL.crl | \
  awk '/Serial Number:/ {print $3}' | \
  cut -d'=' -f2 | \
  sed 's/../&:/g;s/:$//' >> serialnumbers.txt
```

Convert the serial numbers to KeyValueStore JSON format:

```
jq -R -s 'split("\n") | map(select(length > 0)) | {data: map({"key": ., "value": ""})}' \
  serialnumbers.txt >> serialnumbers_kvs.json
```

Upload the formatted file to S3 and create the KeyValueStore as described in Step 1.

## Handle multiple Certificate Authorities
<a name="handle-multiple-cas"></a>

When your TrustStore contains multiple Certificate Authorities (CAs), include the issuer information in your KeyValueStore keys to avoid conflicts between certificates from different CAs that might have the same serial number.

For multi-CA scenarios, use a combination of the issuer's SHA1 hash and the serial number as the key:

```
import cf from 'cloudfront';

async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    const clientCert = connection.clientCertInfo;
    
    // Create composite key with issuer hash and serial number
    const issuer = clientCert.issuer.replace(/[^a-zA-Z0-9]/g, '').substring(0, 20);
    const serialno = clientCert.serialNumber;
    const compositeKey = `${issuer}_${serialno}`;
    
    const cert_revoked = await kvsHandle.exists(compositeKey);
    
    if (cert_revoked) {
        console.log(`Blocking revoked cert: ${serialno} from issuer: ${issuer}`);
        connection.deny();
    } else {
        connection.allow();
    }
}
```

**Note**  
Using issuer identifier \$1 serial number creates longer keys, which may reduce the total number of entries you can store in the KeyValueStore.

## Add custom data to connection logs
<a name="add-custom-data-logs"></a>

Connection functions can add custom data to CloudFront connection logs using the logCustomData method. This lets you include revocation check results, certificate information, or other relevant data in your logs.

```
async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    const clientSerialNumber = connection.clientCertInfo.serialNumber;
    const serialNumberExistsInKvs = await kvsHandle.exists(clientSerialNumber);
    
    if (serialNumberExistsInKvs) {
        // Log revocation details to connection logs
        connection.logCustomData(`REVOKED:${clientSerialNumber}:DENIED`);
        console.log("Connection denied - certificate revoked");
        return connection.deny();
    }
    
    // Log successful validation
    connection.logCustomData(`VALID:${clientSerialNumber}:ALLOWED`);
    console.log("Connection allowed");
    return connection.allow();
}
```

Custom data is limited to 800 bytes of valid UTF-8 text. If you exceed this limit, CloudFront truncates the data to the nearest valid UTF-8 boundary.

**Note**  
Custom data logging only works when connection logs are enabled for your distribution. If connection logs are not configured, the logCustomData method is a no-op.

## Manage CRL updates
<a name="manage-crl-updates"></a>

Certificate Authorities can issue two types of CRLs:
+ **Full CRLs**: Contain a complete list of all revoked certificates
+ **Delta CRLs**: Only list certificates revoked since the last full CRL

For full CRL updates, create a new KeyValueStore with the updated data and redirect the Connection Function association to the new KeyValueStore. This approach is simpler than calculating differences and performing incremental updates.

For delta CRL updates, use the update-keys command to add new revoked certificates to the existing KeyValueStore:

```
aws cloudfront update-key-value-store \
  --name "revoked-serials-kvs" \
  --if-match "current-etag" \
  --put file://delta-revoked-serials.json
```

## Plan KeyValueStore capacity
<a name="plan-kvs-capacity"></a>

KeyValueStore has a 5 MB size limit and supports up to 10 million key-value pairs. Plan your revocation list capacity based on your key format and data size:
+ **Serial number only**: Efficient storage for simple revocation checking
+ **Issuer identifier \$1 serial number**: Longer keys for multi-CA environments

For large revocation lists, consider implementing a tiered approach where you maintain separate KeyValueStores for different certificate categories or time periods.

# 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)
```

------

# Restrictions on edge functions
<a name="edge-functions-restrictions"></a>

The following topics describe the restrictions that apply to CloudFront Functions and Lambda@Edge. Some restrictions apply to all edge functions, while others apply only to CloudFront Functions or Lambda@Edge.

Each topic provides detailed information about the limitations and constraints you should consider when you develop and deploy edge functions with CloudFront. 

Understanding these restrictions helps you ensure that your edge functions operate as expected and comply with the supported features.

**Topics**
+ [

# Restrictions on all edge functions
](edge-function-restrictions-all.md)
+ [

# Restrictions on CloudFront Functions
](cloudfront-function-restrictions.md)
+ [

# Restrictions on Lambda@Edge
](lambda-at-edge-function-restrictions.md)

For information about quotas (formerly referred to as limits), see [Quotas on CloudFront Functions](cloudfront-limits.md#limits-functions) and [Quotas on Lambda@Edge](cloudfront-limits.md#limits-lambda-at-edge).

# Restrictions on all edge functions
<a name="edge-function-restrictions-all"></a>

The following restrictions apply to all edge functions, both CloudFront Functions and Lambda@Edge.

**Topics**
+ [

## AWS account ownership
](#function-restrictions-account-ownership)
+ [

## Combining CloudFront Functions with Lambda@Edge
](#function-restrictions-combining-functions)
+ [

## HTTP status codes
](#function-restrictions-status-codes)
+ [

## HTTP headers
](#function-restrictions-headers)
+ [

## Query strings
](#function-restrictions-query-strings)
+ [

## URI
](#function-restrictions-uri)
+ [

## URI, query string, and headers encoding
](#function-restrictions-encoding)
+ [

## Microsoft Smooth Streaming
](#function-restrictions-microsoft-smooth-streaming)
+ [

## Tagging
](#function-restrictions-tagging)

## AWS account ownership
<a name="function-restrictions-account-ownership"></a>

To associate an edge function with a CloudFront distribution, the function and distribution must be owned by the same AWS account.

## Combining CloudFront Functions with Lambda@Edge
<a name="function-restrictions-combining-functions"></a>

For a given cache behavior, the following restrictions apply:
+ Each event type (viewer request, origin request, origin response, and viewer response) can have only one edge function association.
+ You can’t combine CloudFront Functions and Lambda@Edge in viewer events (viewer request and viewer response).

All other combinations of edge functions are allowed. The following table explains the allowed combinations.

[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-function-restrictions-all.html)

## HTTP status codes
<a name="function-restrictions-status-codes"></a>

CloudFront doesn't invoke edge functions for viewer response events when the origin returns HTTP status code 400 or higher.

Lambda@Edge functions for origin response events are invoked for *all* origin responses, including when the origin returns HTTP status code 400 or higher. For more information, see [Update HTTP responses in origin response triggers](lambda-generating-http-responses.md#lambda-updating-http-responses).

## HTTP headers
<a name="function-restrictions-headers"></a>

Certain HTTP headers are disallowed, which means they're not exposed to edge functions and functions can't add them. Other headers are read-only, which means functions can read them but can't add, modify, or delete them.

**Topics**
+ [

### Disallowed headers
](#function-restrictions-disallowed-headers)
+ [

### Read-only headers
](#function-restrictions-read-only-headers)

### Disallowed headers
<a name="function-restrictions-disallowed-headers"></a>

The following HTTP headers are not exposed to edge functions, and functions can't add them. If your function adds one of these headers, it fails CloudFront validation and CloudFront returns HTTP status code 502 (Bad Gateway) to the viewer.
+ `Connection` 
+ `Expect`
+ `Keep-Alive`
+ `Proxy-Authenticate`
+ `Proxy-Authorization`
+ `Proxy-Connection`
+ `Trailer`
+ `Upgrade`
+ `X-Accel-Buffering`
+ `X-Accel-Charset`
+ `X-Accel-Limit-Rate`
+ `X-Accel-Redirect`
+ `X-Amz-Cf-*`
+ `X-Amzn-Auth`
+ `X-Amzn-Cf-Billing`
+ `X-Amzn-Cf-Id`
+ `X-Amzn-Cf-Xff`
+ `X-Amzn-Errortype`
+ `X-Amzn-Fle-Profile`
+ `X-Amzn-Header-Count`
+ `X-Amzn-Header-Order`
+ `X-Amzn-Lambda-Integration-Tag`
+ `X-Amzn-RequestId`
+ `X-Cache`
+ `X-Edge-*`
+ `X-Forwarded-Proto`
+ `X-Real-IP`

### Read-only headers
<a name="function-restrictions-read-only-headers"></a>

The following headers are read-only. Your function can read them and use them as input to the function logic, but it can't change the values. If your function adds or edits a read-only header, the request fails CloudFront validation and CloudFront returns HTTP status code 502 (Bad Gateway) to the viewer.

#### Read-only headers in viewer request events
<a name="function-restrictions-read-only-headers-viewer-request"></a>

The following headers are read-only in viewer request events.
+ `CDN-Loop`
+ `Content-Length`
+ `Host`
+ `Transfer-Encoding`
+ `Via`

#### Read-only headers in origin request events (Lambda@Edge only)
<a name="function-restrictions-read-only-headers-origin-request"></a>

The following headers are read-only in origin request events, which exist only in Lambda@Edge.
+ `Accept-Encoding`
+ `CDN-Loop`
+ `Content-Length`
+ `If-Modified-Since`
+ `If-None-Match`
+ `If-Range`
+ `If-Unmodified-Since`
+ `Transfer-Encoding`
+ `Via`

#### Read-only headers in origin response events (Lambda@Edge only)
<a name="function-restrictions-read-only-headers-origin-response"></a>

The following headers are read-only in origin response events, which exist only in Lambda@Edge.
+ `Transfer-Encoding`
+ `Via`

#### Read-only headers in viewer response events
<a name="function-restrictions-read-only-headers-viewer-response"></a>

The following headers are read-only in viewer response events for both CloudFront Functions and Lambda@Edge.
+ `Warning`
+ `Via`

The following headers are read-only in viewer response events for Lambda@Edge.
+ `Content-Length`
+ `Content-Encoding`
+ `Transfer-Encoding`

## Query strings
<a name="function-restrictions-query-strings"></a>

The following restrictions apply to functions that read, update, or create a query string in a request URI.
+ (Lambda@Edge only) To access the query string in an origin request or origin response function, your cache policy or origin request policy must be set to **All** for **Query strings**.
+ A function can create or update a query string for viewer request and origin request events (origin request events exist only in Lambda@Edge).
+ A function can read a query string, but cannot create or update one, for origin response and viewer response events (origin response events exist only in Lambda@Edge).
+ If a function creates or updates a query string, the following restrictions apply:
  + The query string can't include spaces, control characters, or the fragment identifier (`#`).
  + The total size of the URI, including the query string, must be less than 8,192 characters.
  + We recommend that you use percent encoding for the URI and query string. For more information, see [URI, query string, and headers encoding](#function-restrictions-encoding).

## URI
<a name="function-restrictions-uri"></a>

If a function changes the URI for a request, that doesn't change the cache behavior for the request or the origin that the request is forwarded to.

The total size of the URI, including the query string, must be less than 8,192 characters.

## URI, query string, and headers encoding
<a name="function-restrictions-encoding"></a>

The values for the URI, query string, and headers that are passed to edge functions are UTF-8 encoded. Your function should use UTF-8 encoding for the URI, query string, and header values that it returns. Percent encoding is compatible with UTF-8 encoding.

The following list explains how CloudFront handles encoding for the URI, query string, and headers:
+ When values in the request are UTF-8 encoded, CloudFront forwards the values to your function without changing them.
+ When values in the request are [ISO-8859-1 encoded](https://en.wikipedia.org/wiki/ISO/IEC_8859-1), CloudFront converts the values to UTF-8 encoding before forwarding them to your function.
+ When values in the request are encoded using some other character encoding, CloudFront assumes that they're ISO-8859-1 encoded and tries to convert from ISO-8859-1 to UTF-8.
**Important**  
The converted characters might be an inaccurate interpretation of the values in the original request. This might cause your function or your origin to produce an unintended result.

The values for the URI, query string, and headers that CloudFront forwards to your origin depend on whether a function changes the values:
+ If a function doesn't change the URI, query string, or header, CloudFront forwards the values that it received in the request to your origin.
+ If a function changes the URI, query string, or header, CloudFront forwards the UTF-8 encoded values.

## Microsoft Smooth Streaming
<a name="function-restrictions-microsoft-smooth-streaming"></a>

You can't use edge functions with a CloudFront distribution that you're using for streaming media files that you've transcoded into the Microsoft Smooth Streaming format.

## Tagging
<a name="function-restrictions-tagging"></a>

You can't add tags to edge functions. For more information about tagging in CloudFront, see [Tag a distribution](tagging.md).

# Restrictions on CloudFront Functions
<a name="cloudfront-function-restrictions"></a>

The following restrictions apply only to CloudFront Functions.

**Contents**
+ [

## Logs
](#cloudfront-function-restrictions-logs)
+ [

## Request body
](#cloudfront-function-restrictions-request-body)
+ [

## Using temporary credentials with the CloudFront KeyValueStore API
](#regional-endpoint-for-key-value-store)
+ [

## Runtime
](#cloudfront-function-runtime-restrictions)
+ [

## Compute utilization
](#cloudfront-function-restrictions-compute-utilization)

For information about quotas (formerly referred to as limits), see [Quotas on CloudFront Functions](cloudfront-limits.md#limits-functions).

## Logs
<a name="cloudfront-function-restrictions-logs"></a>

Function logs in CloudFront Functions are truncated at 10 KB.

## Request body
<a name="cloudfront-function-restrictions-request-body"></a>

CloudFront Functions can't access the body of the HTTP request.

## Using temporary credentials with the CloudFront KeyValueStore API
<a name="regional-endpoint-for-key-value-store"></a>

You can use AWS Security Token Service (AWS STS) to generate temporary security credentials (also known as *session tokens*). Session tokens allow you to temporarily assume an AWS Identity and Access Management (IAM) role so that you can access AWS services.

To call the [CloudFront KeyValueStore API](https://docs.aws.amazon.com/cloudfront/latest/APIReference/API_Operations_Amazon_CloudFront_KeyValueStore.html), use a *Regional* endpoint in AWS STS to return a *version 2* session token. If you use the *global* endpoint for AWS STS (`sts.amazonaws.com`), AWS STS will generate a *version 1* session token, which isn't supported by Signature Version 4A (SigV4A). As a result, you will receive an authentication error.

To call the CloudFront KeyValueStore API, you can use the following options: 

**AWS CLI and AWS SDKs**  
You can configure the AWS CLI or an AWS SDK to use Regional AWS STS endpoints. For more information, see [AWS STS Regionalized endpoints](https://docs.aws.amazon.com/sdkref/latest/guide/feature-sts-regionalized-endpoints.html) in the *AWS SDK and Tools Reference Guide*.  
For more information about available AWS STS endpoints, see [Regions and endpoints](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html#id_credentials_region-endpoints) in the *IAM User Guide*.

**SAML**  
You can configure SAML to use Regional AWS STS endpoints. For more information, see the [How to use regional SAML endpoints for failover](https://aws.amazon.com/blogs/security/how-to-use-regional-saml-endpoints-for-failover/) blog post.

**`SetSecurityTokenServicePreferences` API**  
Instead of using a Regional AWS STS endpoint, you can configure the global endpoint for AWS STS to return version 2 session tokens. To do so, use the [SetSecurityTokenServicePreferences](https://docs.aws.amazon.com/IAM/latest/APIReference/API_SetSecurityTokenServicePreferences.html) API operation to configure your AWS account.   

**Example: IAM CLI command**  

```
aws iam set-security-token-service-preferences --global-endpoint-token-version v2Token
```
We recommend that you use the AWS STS Regional endpoints instead of this option. Regional endpoints provide higher availability and failover scenarios.

**Custom identity provider**  
If you're using a custom identity provider that does the federation and assumes the role, use one of the previous options for the parent identity provider system that is responsible for generating the session token.

## Runtime
<a name="cloudfront-function-runtime-restrictions"></a>

The CloudFront Functions runtime environment doesn't support dynamic code evaluation, and it restricts access to the network, file system, environment variables, and timers. For more information, see [Restricted features](functions-javascript-runtime-10.md#writing-functions-javascript-features-restricted-features).

**Note**  
To use CloudFront KeyValueStore, your CloudFront function must use [JavaScript runtime 2.0](functions-javascript-runtime-20.md).

## Compute utilization
<a name="cloudfront-function-restrictions-compute-utilization"></a>

CloudFront Functions have a limit on the time they can take to run, measured as *compute utilization*. Compute utilization is a number between 0 and 100 that indicates the amount of time that the function took to run as a percentage of the maximum allowed time. For example, a compute utilization of 35 means that the function completed in 35% of the maximum allowed time.

When you [test a function](test-function.md), you can see the compute utilization value in the output of the test event. For production functions, you can view the [compute utilization metric](viewing-cloudfront-metrics.md#monitoring-console.cloudfront-functions) on the [Monitoring page in the CloudFront console](https://console.aws.amazon.com/cloudfront/v4/home?#/monitoring), or in CloudWatch.

# Restrictions on Lambda@Edge
<a name="lambda-at-edge-function-restrictions"></a>

The following restrictions apply only to Lambda@Edge.

**Contents**
+ [

## DNS resolution
](#lambda-at-edge-restrictions-dns)
+ [

## HTTP status codes
](#lambda-at-edge-restrictions-status-codes)
+ [

## Lambda function version
](#lambda-at-edge-restrictions-version)
+ [

## Lambda Region
](#lambda-at-edge-restrictions-region)
+ [

## Lambda role permissions
](#lambda-at-edge-restrictions-role-permissions)
+ [

## Lambda features
](#lambda-at-edge-restrictions-features)
+ [

## Supported runtimes
](#lambda-at-edge-restrictions-runtime)
+ [

## CloudFront headers
](#lambda-at-edge-restrictions-cloudfront-headers)
+ [

## Restrictions on the request body with the include body option
](#lambda-at-edge-restrictions-request-body)
+ [

## Response timeout and keep-alive timeout (custom origins only)
](#timeout-for-lambda-edge-functions)

For information about quotas, see [Quotas on Lambda@Edge](cloudfront-limits.md#limits-lambda-at-edge).

## DNS resolution
<a name="lambda-at-edge-restrictions-dns"></a>

CloudFront performs a DNS resolution on the origin domain name *before* it executes your origin request Lambda@Edge function. If the DNS service for your domain is experiencing issues and CloudFront can't resolve the domain name to get the IP address, your Lambda@Edge function will not invoke. CloudFront will return an [HTTP 502 status code (Bad Gateway)](http-502-bad-gateway.md) to the client. For more information, see [DNS error (`NonS3OriginDnsError`)](http-502-bad-gateway.md#http-502-dns-error).

If your function logic modifies the origin domain name, CloudFront will perform another DNS resolution on the updated domain name after the function has finished executing.

For more information about managing DNS failover, see [Configuring DNS failover](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-failover-configuring.html) in the *Amazon Route 53 Developer Guide*.

## HTTP status codes
<a name="lambda-at-edge-restrictions-status-codes"></a>

Lambda@Edge functions for viewer response events cannot modify the HTTP status code of the response, regardless of whether the response came from the origin or the CloudFront cache.

## Lambda function version
<a name="lambda-at-edge-restrictions-version"></a>

You must use a numbered version of the Lambda function, not `$LATEST` or aliases.

## Lambda Region
<a name="lambda-at-edge-restrictions-region"></a>

The Lambda function must be in the US East (N. Virginia) Region.

## Lambda role permissions
<a name="lambda-at-edge-restrictions-role-permissions"></a>

The IAM execution role associated with the Lambda function must allow the service principals `lambda.amazonaws.com` and `edgelambda.amazonaws.com` to assume the role. For more information, see [Set up IAM permissions and roles for Lambda@Edge](lambda-edge-permissions.md).

## Lambda features
<a name="lambda-at-edge-restrictions-features"></a>

The following Lambda features are not supported by Lambda@Edge:
+ [Lambda runtime management configurations](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-update.html#runtime-management-controls) other than **Auto** (default)
+ Configuration of your Lambda function to access resources inside your VPC
+ [Lambda function dead letter queues](https://docs.aws.amazon.com/lambda/latest/dg/invocation-async.html#dlq)
+ [Lambda environment variables](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html) (except for reserved environment variables, which are automatically supported)
+ Lambda functions with [Managing AWS Lambda dependencies with layers](https://docs.aws.amazon.com/lambda/latest/dg/chapter-layers.html)
+ [Using AWS X-Ray](https://docs.aws.amazon.com/lambda/latest/dg/lambda-x-ray.html)
+ Lambda provisioned concurrency
**Note**  
Lambda@Edge functions share the same [Regional concurrency](https://docs.aws.amazon.com/lambda/latest/dg/configuration-concurrency.html) capabilities as all Lambda functions. For more information, see [Quotas on Lambda@Edge](cloudfront-limits.md#limits-lambda-at-edge).
+ [Create a Lambda function using a container image](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html)
+ [Lambda functions that use the arm64 architecture](https://docs.aws.amazon.com/lambda/latest/dg/foundation-arch.html)
+ Lambda functions with more than 512 MB of ephemeral storage
+ Using a [customer managed key to encrypt your .zip deployment packages](https://docs.aws.amazon.com/lambda/latest/dg/encrypt-zip-package.html)

## Supported runtimes
<a name="lambda-at-edge-restrictions-runtime"></a>

Lambda@Edge supports the latest versions of Node.js and Python runtimes. For a list of supported versions and their future deprecation dates, see [Supported runtimes](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html#runtimes-supported) in the *AWS Lambda Developer Guide*.

**Tip**  
As a best practice, use the latest versions of the provided runtimes for performance improvements and new features.
You can’t create or update functions with deprecated versions of Node.js. You can only associate existing functions with these versions with CloudFront distributions. Functions with these versions that are associated with distributions will continue to run. However, we recommend that you move your function to newer versions of Node.js. For more information, see [Runtime deprecation policy](https://docs.aws.amazon.com/lambda/latest/dg/runtime-support-policy.html) in the *AWS Lambda Developer Guide* and the [Node.js release schedule](https://github.com/nodejs/Release#release-schedule) on GitHub.

## CloudFront headers
<a name="lambda-at-edge-restrictions-cloudfront-headers"></a>

Lambda@Edge functions can read, edit, remove, or add any of the CloudFront headers listed in [Add CloudFront request headers](adding-cloudfront-headers.md).

**Notes**  
If you want CloudFront to add these headers, you must configure CloudFront to add them by using a [cache policy](controlling-the-cache-key.md) or [origin request policy](controlling-origin-requests.md).
CloudFront adds the headers *after* the viewer request event, which means the headers aren't available to Lambda@Edge functions in a viewer request. The headers are only available to Lambda@Edge functions in an origin request and origin response.
If the viewer request includes headers that have these names, and you configured CloudFront to add these headers using a [cache policy](controlling-the-cache-key.md) or [origin request policy](controlling-origin-requests.md), then CloudFront overwrites the header values that were in the viewer request. Viewer-facing functions see the header value from the viewer request, while origin-facing functions see the header value that CloudFront added.
If a viewer request function adds the `CloudFront-Viewer-Country` header, it fails validation and CloudFront returns HTTP status code 502 (Bad Gateway) to the viewer.

## Restrictions on the request body with the include body option
<a name="lambda-at-edge-restrictions-request-body"></a>

When you choose the **Include Body** option to expose the request body to your Lambda@Edge function, the following information and size limits apply to the portions of the body that are exposed or replaced.
+ CloudFront always base64 encodes the request body before exposing it to Lambda@Edge.
+ If the request body is large, CloudFront truncates it before exposing it to Lambda@Edge, as follows:
  + For viewer request events, the body is truncated at 40 KB.
  + For origin request events, the body is truncated at 1 MB.
+ If you access the request body as read-only, CloudFront sends the full original request body to the origin.
+ If your Lambda@Edge function replaces the request body, the following size limits apply to the body that the function returns:
  + If the Lambda@Edge function returns the body as plain text:
    + For viewer request events, the body limit is 40 KB.
    + For origin request events, the body limit is 1 MB.
  + If the Lambda@Edge function returns the body as base64 encoded text:
    + For viewer request events, the body limit is 53.2 KB.
    + For origin request events, the body limit is 1.33 MB.

**Note**  
If your Lambda@Edge function returns a body that exceeds these limits, your request will fail with an HTTP 502 status code ([Lambda validation error](http-502-bad-gateway.md#http-502-lambda-validation-error)). We recommend that you update your Lambda@Edge function so that the body doesn't exceed these limits.

## Response timeout and keep-alive timeout (custom origins only)
<a name="timeout-for-lambda-edge-functions"></a>

If you're using Lambda@Edge functions to set the response timeout or keep-alive timeout for your distribution origins, verify that you're specifying a value that your origin can support. For more information, see [Response and keep-alive timeout quotas](DownloadDistValuesOrigin.md#response-keep-alive-timeout-quota).