Networking & Content Delivery

Amazon CloudFront introduces Response Headers Policies

Introduction

Amazon CloudFront is a content delivery network (CDN) that delivers static and dynamic web content using a global network of edge locations. Customers benefit from better performance, reliability, and increased security of their web applications by including CloudFront in their architecture. The ability to easily modify and manage response headers has been a common ask from the customers. CloudFront introduced response headers policies to address this need and give the customers more control in defining header modifications performed by CloudFront. While it has been possible to manipulate response headers with CloudFront’s edge serverless options, typically it doesn’t require a custom logic unique to the use case. In this blog post you will find how response headers policies simplify the process of manipulating the response headers, the headers supported by the policies, and how you can integrate policies into your workloads.

How do response headers policies fit into the CloudFront workflow?

With Amazon CloudFront you can deploy your own custom logic at the edge by choosing between two edge serverless environments. For more complex and compute intense operations Lambda@Edge is often the right solution, while CloudFront Functions makes a good alternative for simple request and response manipulations at the very large scale. To learn more about unique characteristics of each options and corresponding use cases, review CloudFront Functions announcement blog post. A common pattern in response manipulation category is to insert response headers before serving the response to the viewers, with little or no conditionality. Typically, this involves inserting widely used headers such as cross origin resource sharing (CORS) headers, or security headers like Strict Transport Security (HSTS) that specify the security-related details of communications between the browser and the server. Customers also add missing headers that the origins can’t generate by default, or just pass a static header carrying additional information relevant to the application run by the clients. Today, they use CloudFront functions for this. While CloudFront functions is a well suited environment to support that, it creates an overhead for the customers when they need to extend their code development practices to the CDN part of the architecture. It can also take some effort to keep consistency in applying common set of headers across different workflows.

Response headers policies simplify the process of HTTP header response manipulation so that you can define CORS, security, and custom response headers as a configuration setting in CloudFront through the console or the API. You can define multiple combinations of the header sets and keep them as separate and reusable policies. These policies can then be associated with one or more behaviors to achieve the desired application functionality. Adding headers through response headers policies can work together with Lambda@Edge or CloudFront Functions if the response requires additional processing. For this type of workflow, it is important to remember the order in which the response is processed, especially if response headers policy and your code logic depend on the same headers. The following diagram illustrates the order of operations between caching layers, response headers policies, and Lambda@Edge or CloudFront Functions. Note that only one function (either Lambda@Edge or CloudFront Function) can be associated with the Viewer Response trigger for any given behavior at a time.

Response Header Policies workflow

Figure 1 Response headers policies workflow

As outlined in the preceding diagram, response headers policies do not impact the origin-supplied headers stored in CloudFront’s caching layers. Headers configured in the policies are inserted after the response leaves the cache, and before the viewer response event that triggers a function if configured. If you have an edge function attached to the same behavior, policy inserted headers will be accessible in your function through the event object listing all the headers associated with the response. You can use that functionality by treating the headers generated through a policy as inputs for the function that will impact how the code is executed. This is similar to using environmental variables.

Anatomy of response headers policies

Response header settings are organized into policies, a configuration construct introduced with the Cache and Origin Request policies. Customers use policies is to make it easier to define and manage CloudFront settings by grouping them into reusable configuration sets. The high-level process of defining and managing the response headers policies would normally look as follows:

  • Start by creating policies that match requirements of different workloads, for example one policy with more restrictive settings for API endpoints to be used by trusted domains and second one for static assets you want to share publicly.
  • Next, apply each policy across multiple CloudFront distributions and behaviors in your account.
  • If the requirements change, and you need to add more trusted domains for API workloads, edit the corresponding policy and the updated settings will take effect on all cache behaviors linked to it.
  • When you add more CloudFront distributions and cache behaviors, verify if any of the existing policies can be applied on them. If not, create a separate policy with the required headers settings and associate it with the new cache behaviors.

To review existing or create a new policy you can access them from Policies page listed in the navigation panel, as shown in the following screenshot.

Response Header Policies list in the CloudFront console

Figure 2 Response headers policies in CloudFront console

On top of the response headers policy page customers can find a list of managed policies with different combinations of generic header configurations where customers can apply basic settings quickly. While the specifics of these policies have been curated with special care to help avoid disrupting the viewer’s experience, make sure to review what each policy does and what headers are inserted. This will ensure your web application aligns with it well. Because browsers can cache information in these headers for longer period of time, reverting the effects of implementing some of the headers in the policies can require some time to take effect on a viewer’s behavior. Each policy, managed or custom, is comprised of three distinct sections that govern separate sets of response headers.

Response Headers Policy structure

Figure 3 A custom response header policy structure

Cross-origin resource sharing (CORS)

Cross-origin resource sharing is a mechanism used by browsers and web application origins to conditionally allow requests that would normally violate same-origin policy restrictions. These restrictions were established to protect the users visiting a website from triggering unintended requests to third party domains. For example, by malicious script injected on the website that sends the instructions for new transaction or collect sensitive information. As an owner of a web application, same-origin policy principle prevents you from receiving potentially fraudulent requests harming your users. However, there are various use cases when you want to deliberately relax this restriction to allow cross-domain requests initiated from trusted domains. By inserting appropriate CORS response headers you instruct the web browser what is the allowed scope of the requests, with the conditions to be met, in order to permit request and response. There are six CORS related response headers pre-defined in the response header policy, applicable both for simple and pre-flight requests. Now, you can define multiple origins (as in source domains) without needing to include the “Origin” header in the cache key definition, which decreases the cache hit rate.

Security headers

In addition to CORS headers, there are other well-known headers you can add to improve the security of your website. The headers grouped in this section serve as directives for the browser on the restrictions it should follow in the context of your website. For example, setting Strict-Transport-Security header dictates that the browser will upgrade the connection to HTTPS for all the subsequent requests the browser makes for any page on the same domain, even if the user types http:// in the address bar. Including each of these headers is optional and you can decide which of them you want to use. Six security headers that can be included in the policy are

  • HTTP Strict Transport Security (HSTS)
  • X-XSS-Protection
  • X-Content-Type-Options
  • X-Frame-Options
  • Referrer-Policy
  • Content-Security-Policy

Custom headers

There is a long-tail of use cases that require adding other type of headers. As these use cases vary depending on the application context, custom headers allow defining static response headers as simple key-value pairs. Use cases for custom headers include:

  • inserting or overwrite cache-control for downstream caches and browsers
  • tagging the response with CDN identifier or application version
  • conveying origin metadata
  • adding compatibility information

Note: It is also possible that the headers you specify in the response header policies can conflict with the headers supplied by the origin. Specifically, when CloudFront distribution operates with multiple origins there could be inconsistencies in what particular set of headers each origin sets and when. To address that, by using response header policies you can configure how to manage overlapping headers, either by overriding the response header coming from the origin or by omitting header insertion if the original response already includes it and you keep the override option disabled.

Input validation and CORS headers conditionality

You may wonder why the response headers policies have been structured with three separate sections, with the first two containing limited set of predefined headers instead of keeping policy as a simple list of key-value pairs. There are two important benefits that stem from the separation of the two security header sets:

  • Input validation – most of these well-known headers have finite set of values they can assume and are prone to errors when entered manually. By restricting the number of possible options, we limit the possibility of accidentally producing a malformed header, which can lead to unexpected behaviors on your website.
  • CORS conditionality – when it comes to managing the CORS headers there are specific rules mandated by the Fetch standard regarding correct header combinations and their values. CloudFront implements additional logic for Response Headers Policies to ensure the right CORS headers are returned based on the request header details.

As an example of this, let’s consider how the CORS conditionality works with the Access-Control-Allow-Origin header. This is one of the key headers in context of CORS, which tells the browser what domains are allowed to load the content from your website. If there are multiple domains that you want to allow, you should submit the list of them as an input in the CORS section. However, CORS specification dictates that only one value can be returned in that header. Therefore, CloudFront’s host will determine the domain from which the request originated through the Origin header and validate that against the list of defined values for Access-Control-Allow-Origin.

For example, assume CORS headers in the policy are defined as follows:

Defining multiple origins in CORS headers configuration

Figure 4 Multiple origins in CORS headers configuration

With a simple CORS request made, only one of these two values will be returned accordingly:

$curl -I -H "Origin: https://aws.amazon.com" https://d12345abcde789.cloudfront.net

HTTP/1.1 200 OK
(...)
Access-Control-Allow-Origin: https://aws.amazon.com
Vary: Origin

Associating policies to cache behavior

After you create a custom policy or select one from the list of managed policies, the next step is to associate it with the right cache behaviors. The configuration workflow is very similar to Cache and Origin Request policies experience. Once you navigate to the cache behavior settings, Response header policies list is available under Cache Key and origin request configuration from which you can select the one that will be used for this behavior.

Associating response headers policies to cache behaviors

Figure 5 Associating response headers policies to cache behaviors

Note that response header policy is an optional setting. There will not be any default policy applied to any of your existing behaviors, or new behaviors, after the feature launch. Once the policy is associated with a behavior, within the time it takes for CloudFront configuration changes to propagate, you will see the effects of it on the response headers returned.

$curl -I -H "Origin: https://example.com" https://d12345abcde789.cloudfront.net
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 90
##security headers##
x-xss-protection: 1; mode=block; report=http://example.com/report
x-frame-options: SAMEORIGIN
referrer-policy: strict-origin-when-cross-origin
content-security-policy: default-src https:
x-content-type-options: nosniff
strict-transport-security: max-age=31536000
##custom headers##
x-cdn: CloudFront
##CORS headers##
access-control-allow-credentials: true
access-control-allow-origin: https://example.com
vary: Origin
access-control-expose-headers: X-ExposedHeader

X-Cache: Miss from cloudfront
Via: 1.1 1e4c7e8afcaf6e21a729c3a37d8d3094.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: PHX50-C2
X-Amz-Cf-Id: Vn5R_GXTLzBmb2rhJATR6iii8vSzHCgeMDoYgZzJ3QFb_-Hdszvkng==

Summary

In this post, we introduced response headers policies as a step forward in streamlining response manipulations at the edge without the need to manage code. These policies decouple the response header modifications from serverless environments such as Lambda@Edge, or CloudFront Functions that can now be tailored for more advanced logic. With the three categories of headers supported in the policies – CORS (with additional logic to comply with the standard), security, custom headers, there is enough flexibility for customers to support a variety of workloads with different Origin types. Customers can define response headers policies once, and reuse them across multiple applications and CloudFront distributions. Since the policies apply to cache behaviors, customers can define the response headers granularly for separate parts of their applications, or different origin types. Ready to get started? CloudFront response headers policies are available for immediate use via the CloudFront Console, the AWS SDKs, and the AWS CLI. For more information, refer to the CloudFront Developer Guide.

About the authors

Kamil Bogacz

Kamil is an Edge Specialist Solutions Architect at AWS. He advises customers across industries on building performance optimized and secure solutions with AWS Edge Services.

Megha Kande

Megha is a Product Manager on the Amazon CloudFront team in Seattle, WA.