Published on: Sun Nov 20 2022
When working with Amazon Cloudfront, the properties in the request are not automatically passed to your origin server.
This is by design, and one way to customize what gets passed over is using the origin request policy.
In this tutorial, we will go through the steps on how to set this up using terraform!
For this tutorial, we will be building on our code in Amazon Cloudfront: Create a distribution + Lambda (tutorial).
Let’s jump right into it
Before you start going through the tutorial, make sure you are using the respository from the previous tutorial.
The respository can be found here - amazon-cloudfront-create-distribution.
It will be the base from which we will build from!
Here is a big picture overview of some theory and background before we go into the nitty gritty details.
The origin request will always include:
Host
, User-Agent
, X-Amz-Cf-Id
)These are the only properties included.
So, if you want additional properties, we need to use the origin request policy to add them in.
The architecture is similar to our previous tutorial. The only difference is we are customizing our cloudfront configurations here.
Then, by inspecting the logs, we can see the origin request policy in action!
Illustration of the sandbox architecture with Cloudfront and Lambda
The Viewer (client) request comes in
Cloudfront forwards the request to the origin server (Lambda URL + Lambda)
The logs from the request are added into Cloudwatch Logs
The Lambda function returns a response
The Viewer (client) receives the response
In our example, let’s setup the policy to pass over a custom header X-Very-Secret-Client-Token
to our origin server.
In terraform, there is a resource just for this
Add the following changes:
// infra/main.tf
resource "aws_cloudfront_origin_request_policy" "custom" {
name = "custom-origin-request-policy"
comment = "Custom origin request policy"
headers_config {
headers {
items = ["X-Very-Secret-Client-Token"]
}
}
}
The header behavior defines how cloudfront should treat the client request headers.
For our example, we will be using “whitelist” (or allowlist) which is limited to the header values we provide.
However, there are a few options, be sure to check the terraform resource documentation.
Add the following changes:
// infra/main.tf
resource "aws_cloudfront_origin_request_policy" "custom" {
name = "custom-origin-request-policy"
comment = "Custom origin request policy"
headers_config {
header_behavior = "whitelist"
headers {
items = ["X-Very-Secret-Client-Token"]
}
}
}
Like the header config, these fields are a required field so we will just set it to none
for now.
The values are defined in the same way as the headers, check terraform documentions for more information.
// infra/main.tf
resource "aws_cloudfront_origin_request_policy" "custom" {
name = "custom-origin-request-policy"
comment = "Custom origin request policy"
headers_config {
header_behavior = "whitelist"
headers {
items = ["X-Very-Secret-Client-Token"]
}
}
cookies_config {
cookie_behavior = "none"
}
query_strings_config {
query_string_behavior = "none"
}
}
In our case we are attaching to the default cache behavior, which would to all requests.
// infra/main.tf
resource "aws_cloudfront_distribution" "cf_distribution" {
origin {
# This is required because "domain_name" needs to be in a specific format
domain_name = replace(replace(aws_lambda_function_url.test.function_url, "https://", ""), "/", "")
origin_id = module.lambda_origin.lambda[0].function_name
custom_origin_config {
https_port = 443
http_port = 80
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}
}
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = module.lambda_origin.lambda[0].function_name
viewer_protocol_policy = "redirect-to-https"
min_ttl = local.min_ttl
default_ttl = local.default_ttl
max_ttl = local.max_ttl
origin_request_policy_id = aws_cloudfront_origin_request_policy.custom.id
# A cache policy is required when attaching, we’ll use a managed policy id for now
cache_policy_id = "658327ea-f89d-4fab-a63d-7e88639e58f6"
}
price_class = var.cf_price_class
enabled = true
is_ipv6_enabled = true
comment = "origin request policy test"
default_root_object = "index.html"
restrictions {
geo_restriction {
restriction_type = "none"
}
}
tags = {
Environment = "production"
}
viewer_certificate {
cloudfront_default_certificate = true
}
}
Now we have all our definition in Terraform, all that is left is to generate it.
Run the following:
// This will re-generate the assets
pnpm run generate-assets --filter "@function/*"
export AWS_ACCESS_KEY_ID=<your-key>
export AWS_SECRET_ACCESS_KEY=<your-secret>
export AWS_DEFAULT_REGION=us-east-1
terraform init
terraform plan
terraform apply -auto-approve
If the infrastructure applied successfully then you should see something like this:
Illustration of the Terraform outputs
Curl:
curl -vvv -H "X-Very-Secret-Client-Token: secret-token" "<cf_distribution_domain_url>"
Postman:
Illustration of using Postman for the request
Apart from the response from cloudfront, be sure to check the logs of your lambda function (our origin server).
Make you are able to see it as it will be useful later when we start to integrate the origin request policies.
Here is an example of the event that is logged from our origin server.
Request Event Example:
Illustration of the logs when adding a origin request policy
{
"event": {
"version": "2.0",
"routeKey": "$default",
"rawPath": "/test",
"rawQueryString": "",
"headers": {
"x-amzn-tls-version": "TLSv1.2",
"x-forwarded-proto": "https",
"x-forwarded-port": "443",
"x-forwarded-for": "2001:569:7f36:4000:59d2:4451:b85c:dd8d",
"via": "2.0 d7782b26e589b8e1397d352f4daf0d58.cloudfront.net (CloudFront)",
"x-amzn-tls-cipher-suite": "ECDHE-RSA-AES128-GCM-SHA256",
"x-amzn-trace-id": "Root=1-6379c843-5a977794388130833ee2d137",
"host": "qlhr2r65yczsyuiwzwqwdckdqq0eukxl.lambda-url.us-east-1.on.aws",
"x-very-secret-client-token": "secret-token",
"cache-control": "no-cache",
"accept-encoding": "br,gzip",
"x-amz-cf-id": "e7D4qcWlIgzSWXm-EMof5oqJi0tr6Ii21E8RrmlJBiulN0GH27fslg==",
"user-agent": "Amazon CloudFront"
},
"requestContext": {
"accountId": "anonymous",
"apiId": "qlhr2r65yczsyuiwzwqwdckdqq0eukxl",
"domainName": "qlhr2r65yczsyuiwzwqwdckdqq0eukxl.lambda-url.us-east-1.on.aws",
"domainPrefix": "qlhr2r65yczsyuiwzwqwdckdqq0eukxl",
"http": {
"method": "GET",
"path": "/test",
"protocol": "HTTP/1.1",
"sourceIp": "64.252.71.170",
"userAgent": "Amazon CloudFront"
},
"requestId": "7f3331cd-c417-495a-ab56-72891d04c5a4",
"routeKey": "$default",
"stage": "$default",
"time": "20/Nov/2022:06:25:07 +0000",
"timeEpoch": 1668925507591
},
"isBase64Encoded": false
}
}
As you can see our custom header (X-Very-Secret-Client-Token
) is there.
For reference, here is the repository of completed tutorial - Github: amazon-cloudfront-origin-request-policy.
That’s really it for the origin request policy, remember its a policy that allow us to customize the properties passed into the origin server.
And... that’s all for now, stay tuned for more!
If you found this helpful or learned something new, please do share this article with a friend or co-worker 🙏❤️ (Thanks!)
🔥 Challenge:
Try to add query strings and cookies to our existing setup then test it out to see if it get passed over by inspecting the logs.
See if you can get it to work!
Then consider signing up to get notified when new content arrives!