CloudFront with API Gateway authorization headers

API Gateway supports a built-in CloudFront distribution using edge-optimized endpoints. If you want more control over the CloudFront distribution you will need to create your own behind an API Gateway regional endpoint.

This comes with some small challenges when it comes to Authorization headers when they need to be forwarded to the API Gateway origin.

There is an AWS knowledge based article that explains how to do this using a cache policy.

CloudFront Cache Policy - Authorization Header

This works, but what if you want to disable the cache? The article claims that You can't use an origin request policy to forward the Authorization header.. If you attempt to create a custom origin policy with the Authorization header you will get an error as seen below.

CloudFront Origin Policy - Authorization Header error

There is a built-in Managed-AllViewer origin request policy that will forward all headers to the origin including the Authorization header. However, this also forwards the Host header which will be denied by API Gateway and return a 403 Forbidden as it will not match the API endpoint.

There are some workarounds to this:

  1. Use API Gateway custom domain names to pass an expected Host header to API Gateway.
  2. Use Lambda@Edge to re-write the Host header.
  3. Use the cache and set a 0 TTL.

1. Use API Gateway custom domains

You can use API Gateway custom domains to match the custom domain used on the CloudFront distribution to avoid the Host header issue. You can then point your DNS entry to the CloudFront distribution rather than the API Gateway custom domain.

  1. Create an API Gateway regional endpoint.

  2. Create an AWS Certificate Manager (ACM) public certificate in the us-east-1 region (required for CloudFront). If your API Gateway is not in us-east-1 then also create the same certificate in the API Gateway region e.g. eu-west-1.

  3. Create an API Gateway custom domain name (regional) using the created ACM certificate.

  4. Map the custom domain name to the API Gateway stage.

  5. Create a CloudFront distribution using the default endpoint as the origin (without the stage name in the origin path, as this has already been mapped in the custom domain name). Ensure you set the alternate domain name (CNAME) to your custom domain name.

  6. Set the cache policy to CacheDisabled and the origin request policy to AllViewer. CloudFront distribution policy settings

  7. Configure your DNS to point to the new CloudFront distribution.

Now you can make a call to the CloudFront distribution using an Authorizer header.

2. Use Lambda@Edge

Another option is to forward all headers as before using the AllViewer origin request policy, but instead of using API Gateway custom domain names use Lambda@Edge to re-write the Host header.

  1. Create a Lambda function in us-east-1 using Basic Lambda@Edge permissions IAM role template.

  2. Within the Lambda code re-write the Host header.

def lambda_handler(event, context):

    request = event['Records'][0]['cf']['request']
    request['headers']['host'] = [{'key': 'host', 'value': '<your-api-id>.execute-api.<aws-region>.amazonaws.com'}]
    print('Updated host header successfully')
    
    return request
  1. Add a CloudFront trigger to the Lambda function for origin-request and select the CloudFront distribution you created earlier.

Now you can make a call to the CloudFront distribution using an Authorizer header. Be aware that with this method Lambda is invoked on every request as the cache is disabled. This can add latency and increase costs. In my testing the average function duration for the code sample above was 1ms. Cold start (Init) duration was around 100ms.

3. Use the cache and set a 0 TTL

The third option is to create a custom cache policy and set the Minimum TTL and Default TTL to 0. This will essentially disable the cache but forward the Authorization header.

  1. Create a new custom cache policy in CloudFront. Set the following TTL settings:

    • Minimum TTL: 0
    • Maximum TTL: 1 (cannot be 0, but will not be used unless the origin uses Cache-Control headers)
    • Default TTL: 0
  2. Set the cache key headers to include the Authorization header.

cache key settings

  1. Apply the cache policy to the CloudFront distribution by modifying the behavior.

Now you can make a call to the CloudFront distribution using an Authorizer header.