AWS for M&E Blog

Optimizing video delivery and cache efficiency using CORS headers and Amazon CloudFront

Customers often ask how to achieve a high cache efficiency when streaming video to a wide range of devices at scale through a Content Delivery Network (CDN).  Specifically, when the client application is delivered from one endpoint (origin A), and the video content itself is delivered from a second endpoint (origin B). This type of architecture adds complexity that can affect your cache efficiency when some of the target devices require a valid Cross-Origin Resource Sharing (CORS) header to playback content.

Modern browsers enforce a same-origin policy on webpages by default. The same-origin policy is a security feature implemented by browsers to mitigate the risk of certain malicious and cross-site scripting attacks. If your webpage or application is served from one origin (origin A), and needs to reference content from a different origin (origin B), the response from origin B must include the Access-Control-Allow-Origin header, set to the value of origin A’s domain name for the response to be accepted by the browser.

Some platforms, such as iOS and Android, do not enforce ‘same-origin policy’ and may not include the Origin header in the request at all. These variations in the Origin header can affect the efficiency of the content caching in a CDN. A low cache hit rate will result in a higher volume of requests back to the origin, and higher response times to viewers, impacting their quality of experience.

For more information about CORS headers and how they’re used for real-world CORS requests and responses, see cross-origin resource sharing in the MDN Web Docs and the CORS protocol spec.

To solve this issue, you can optimize your CORS response headers using Amazon CloudFront, a content delivery network (CDN) from Amazon Web Services (AWS). Amazon CloudFront is built for high performance, secure delivery, and developer convenience. CloudFront offers 3 different policies – cache, origin request, and response header, that together can help you set the desired cache behavior, and improve your cache hit rate. Specifically for CORS configuration, you can use a response header policy to set CORS headers on top of headers that are being sent from your origin to CloudFront, or override if needed. Using this approach, you can:

  • Simplify your origin configuration by shifting CORS headers to the CloudFront edge
  • Reduce the volume of traffic back to the origin as unique requests are no longer required for each variation in the Origin header
  • Ensure consistent CORS responses are delivered to clients, even when the CloudFront distribution is configured with multiple origins
  • Configure an allowed list of origins, enabling a single CloudFront distribution to service requests from multiple webpages/applications

A common architecture for video delivery with separate application and media origins is to use Amazon Simple Storage Service (Amazon S3) as the origin hosting the client player application, AWS Media Services origin for the video streaming, and CloudFront CDN fronting both origins.

AWS Elemental MediaPackage is a just-in-time video packaging and origination service running in the AWS cloud. With MediaPackage, you can deliver highly secure, scalable, and reliable video streams to a wide variety of playback devices and CDNs. MediaPackage also supports time-shifted viewing and manifest filtering through the use of query parameters. As these query parameters change the content originated by MediaPackage, it is critical that the origin request and cache key settings are appropriately configured to ensure unique assets are cached separately in CloudFront.

Figure 1 – Amazon CloudFront configuration with multiple origins

Figure 1 shows an application architecture with two origins:

Origin A is an Amazon S3 bucket where your application is hosted.
Origin B is an AWS Elemental MediaPackage channel originating a live stream.

In this architecture, the Application Distribution is serving the hosted application in Origin A. In the hosted application, there is a reference to a video stream being served from the Video Distribution.

The Client loads the application and fetches the video stream. If the Client enforces CORS policy, it will only allow the application to play the video stream if the Video Distribution is configured to return the Access-Control-Allow-Origin header, set to the hostname of the Application Distribution when the client enforces a same-origin policy.

You can use an Amazon CloudFront custom response header policy to customize the CORS headers returned in a viewer response based on the viewer request. This moves the CORS handling from the origin to the CloudFront edge, allowing a single object to be cached and used to service requests for all variations of the Origin header.

Furthermore, you can configure the response header policy with an allowed list of origins. When configured, if the Origin header included in the viewer request does not match a value in the allowed list, CloudFront will not include the Access-Control-Allow-Origin header in the viewer response. In this case, playback should be blocked by any clients enforcing a same-origin policy. Using this feature makes it more difficult for unauthorized websites to include a link to your videos, as the browser should block the viewer response.

Most non-browser playback devices do not enforce CORS policies and will still be able to play back content in the absence of the Access-Control-Allow-Origin header. Some devices (e.g. Smart TVs) may use an open source browser framework to render the application displayed to the user, and in these cases, it may be necessary to include the origin of the application in the list of allowed origins for playback to work.

For content requiring more stringent protection, a correct use of CORS is only one layer of a defense in depth strategy. You are encouraged to also use a Token-Based Authentication (see AWS Secure Media Delivery at the Edge) and/or Digital Rights Management (DRM) solution.

Note: CloudFront response header policy accepts a ‘*’ to allow all origins (this sets the Access-Control-Allow-Origin: * header in responses). If your content is intended for wide distribution and you want to allow third party websites to embed your content with no restriction, you can consider using this setting under the Shared Responsibility Model.

CloudFront origin request and cache policy configuration

CloudFront provides you with origin request policies, cache policies, and response header policies in two forms – managed policies with fixed configuration, and custom policies. Managed policies provide presets of common configurations. You can use custom policies to configure your own settings, and apply the same policy across multiple CloudFront distributions.

The following describes the process for creating an origin request policy and cache policy. The settings outlined are intended to be used in conjunction with a response header policy overriding the CORS headers in the CloudFront origin response. After the policies are created, they need to be associated with the behavior in the target CloudFront distribution.

Note: One of the CloudFront managed policies is a Managed-Elemental-MediaPackage cache policy that includes the Origin header in the cache key. This policy is recommended for a case where you use a single end-user DNS domain for both application and media delivery. Using the Managed-Elemental-MediaPackage policy will result in CloudFront caching a unique object for each unique value of the Origin header. This will reduce CloudFront overall cache hit rate and increase origination requests to MediaPackage.

Creating a custom origin request policy

  1. Navigate to the CloudFront service in the AWS Management Console
  2. Select Policies from the navigation pane
  3. Select the Origin request tab
  4. Select Create origin request policy
  5. Enter a policy Name and optionally a Description
  6. In the Query strings dropdown, select Include specific query strings
  7. Add the following query strings to the allow list:
    1. manifestfilter
    2. start
    3. end
    4. m
  8. Select Create

Origin request policy settings screenshot

Figure 2 – Origin request policy settings

Creating a custom cache policy

  1. Navigate to the CloudFront service in the AWS Management Console
  2. Select Policies from the navigation pane
  3. Select the Cache tab
  4. Select Create cache policy
  5. Enter a policy Name and optionally a Description
  6. In the Query strings dropdown select Include specific query strings
  7. Add the following query strings to the allow list:
    1. aws.manifestfilter
    2. start
    3. end
    4. m
  8. Select Create

Cache policy settings screenshot

Figure 3 – Cache policy settings

CloudFront response header policy configuration

A custom response header policy is required to override the Access-Control-Allow-Origin header returned from MediaPackage so a single cached asset can be used to service all requests served by CloudFront regardless of the passed Origin header.

Using the response header policy described below, devices respecting CORS policies will receive the headers necessary to avoid CORS errors if the request is from an origin in the allowed list. Devices not passing an origin value on the allowed list will not receive the additional headers in the response.

Creating a custom response header policy

  1. Navigate to the CloudFront service in the AWS Management Console
  2. Select Policies from the navigation pane
  3. Select the Response headers tab
  4. Select Create response headers policy
  5. Enter a policy Name and optionally a Description
  6. Enable the Configure CORS toggle
  7. Set Access-Control-Allow-Origin to Customize and include all allowed origins in the list (or leave this as ‘All origins’ if desired)
  8. Set Access-Control-Allow-Methods to include only GET, HEAD and OPTIONS as these are the methods supported by MediaPackage
  9. Enable the Override origin checkbox
  10. Select Create

Response headers policy settings screenshot

Figure 4 – Response headers policy settings

Using this configuration:

  • Responses to requests including an Originheader in the list of allowed origins will include the Access-Control-Allow-Origin header set to the value of the specified origin as well as ‘Access-Control-Allow-Methods: GET, HEAD, OPTIONS’
  • Responses to requests including an Originheader not in the list of allowed origins will not include the Access-Control-Allow-Origin or Access-Control-Allow-Methods headers
  • Responses to requests not including an Originheader will not include any CORS headers

Once created, the response header policy needs to be associated with the behaviors in the target CloudFront distribution. See the Using the managed response headers policies documentation for additional details.

Expected CORS policy behavior

­­You can use curl to test your configuration. When a CORS policy has been configured, the CloudFront distribution will include the Access-Control-Allow-Origin header equal to the passed Origin header if the:

  • Request contains an Origin header
  • Origin header in the request is one of the origins specified in the distribution configuration

The following curl command shows ‘Access-Control-Allow-Origin: https://demo.domain1.com’ included in the response when the request Origin header is ‘https://demo.domain1.com’. In this configuration, https://demo.domain1.com is one of the configured allowed origins.

curl -I -H “Origin: https://demo.domain1.com”

https://xxx.cloudfront.net/out/v1/fdee9d9228154202b84e41726ea99ba5/index.m3u8

HTTP/1.1 200 OK
Content-Type: application/x-mpegURL
Content-Length: 626
Connection: keep-alive
Date: Wed, 12 Jan 2022 02:58:34 GMT
Server: nginx/1.18.0
Cache-Control: max-age=2
X-MediaPackage-Manifest-Last-Sequence: 0
X-MediaPackage-Manifest-Last-Updated: 0
X-MediaPackage-Request-Id: Root=1-61de43da-1bb3ae9b3147a5cf40a303b4
Access-Control-Allow-Origin: https://demo.domain1.com
Vary: Origin
X-Cache: Miss from cloudfront
Via: 1.1 5f075e24289dfb9f4306089d01615990.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: SYD4-C2
X-Amz-Cf-Id: 9dL01YUJdCC51_W8qqax9w8kP2BH82-EP_KPIjiwzh2pmJjs2reK1w==

The following curl command shows a request where the specified origin is not an allowed origin. Note how in this case, no Access-Control-Allow-Origin header is included in the response. This is the same behavior as if no Origin header were specified in the request.

curl -I -H “Origin: https://anotherdomain.com”

https://xxx.cloudfront.net/out/v1/fdee9d9228154202b84e41726ea99ba5/index.m3u8

HTTP/1.1 200 OK
Content-Type: application/x-mpegURL
Content-Length: 626
Connection: keep-alive
Date: Wed, 12 Jan 2022 03:03:12 GMT
Server: nginx/1.18.0
Cache-Control: max-age=2
X-MediaPackage-Manifest-Last-Sequence: 0
X-MediaPackage-Manifest-Last-Updated: 0
X-MediaPackage-Request-Id: Root=1-61de44f0-2f49c0f416c7ad45537a3cef
Vary: Origin
X-Cache: Miss from cloudfront
Via: 1.1 c71e51961956d2c084f13451959744de.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: SYD1-C2
X-Amz-Cf-Id: rT4kyBXhXnoFxU921npChK81r_cUiIN9io5GShcbfWjfL3bMwin5fQ==

For a response header policy where Access-Control-Allow-Origin is set to ‘All origins’, CloudFront will include ‘Access-Control-Allow-Origin: *’ in the response only when the request contains an Origin header.

The following curl command shows ‘Access-Control-Allow-Origin: *’ included in the response when the request Origin header is ‘https://anydomain.com’.

curl -I -H “Origin: https://anydomain.com”

https://yyy.cloudfront.net/out/v1/103569af92524c4ba28b62c7f5fa0015/index.m3u8

HTTP/1.1 200 OK
Content-Type: application/x-mpegURL
Content-Length: 626
Connection: keep-alive
Date: Wed, 12 Jan 2022 03:10:00 GMT
Server: nginx/1.18.0
Cache-Control: max-age=2
X-MediaPackage-Manifest-Last-Sequence: 0
X-MediaPackage-Manifest-Last-Updated: 0
X-MediaPackage-Request-Id: Root=1-61de4688-433a36b0211d929e0ea3026f
Access-Control-Allow-Origin: *
X-Cache: Miss from cloudfront
Via: 1.1 35202ecfee8e63e178de36be1b541f0e.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: SYD62-P2
X-Amz-Cf-Id: vxZEQSv1GFIBXHCNSRn9XnInoXHQWbjIIQu7fgaGz83yv7mQAbEjcg==

Summary

CORS headers let you set your cross-origin policy, but can also reduce your cache efficiency if not configured correctly. Use CloudFront response header policies to simplify your CORS across multiple devices, and apply the right CORS headers for your video streaming application.

Configuring a CloudFront response header policy to override the origin CORS header can improve the CDN cache hit rate, minimizing origination from the origin. By optimizing the CDN cache hit rate, the scalability of the workflow is improved and origination costs minimized.

Andrew Fayle

Andrew Fayle

Andrew is a Senior Solution Architect, specializing in AWS Media Services in Australian and New Zealand. He has spent more than ten years building and operating large scale OTT solutions in the Telecommunications industry before joining AWS. Andrew now focuses on enabling customers to build innovative solutions making the most of AWS Elemental Media Services.