You can add custom headers to the response from CloudFront / S3 using a Lambda@Edge function. The lambda code runs within the local edge locations, but needs to be created and maintained in the us-east-1
region.
The example code here uses nodeJS 6.10 to add the x-frame-options
response header, but you can add any header that is not restricted by AWS.
'use strict';
exports.handler = (event, context, callback) => {
const response = event.Records[0].cf.response;
const headers = response.headers;
response.headers['x-frame-options'] = [{"key":"X-Frame-Options","value":"SAMEORIGIN"}];
console.log(response.headers);
callback(null, response);
};
Create a definitive version of the Lambda, then set the Lambda Version's trigger configuration as the CloudFront origin-response
Event type for your path pattern behavior.
The example code logs events to CloudWatch logs service for debugging purposes. If you don't already have one you will need to setup a lambda execution IAM role that allows a policy allowing CloudWatch logs actions to be assumed by edgelambda.amazonaws.com
and lambda.amazonaws.com
.
Basic Lambda Execution Policy allowing logs to be written to CloudWatch:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*",
"Effect": "Allow"
}
]
}
Trust Relationship allowing Lambda and Lambda@Edge to assume the role :
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"edgelambda.amazonaws.com",
"lambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
It would be better if AWS simply allowed the custom headers to be set using the CloudFront GUI but until then this solution should satisfy your requirement.
First of all, you need to upload your custom SSL certificate to Cloudfront in order to avoid issues during the SSL validation. If you do not upload a custom certificate valid for your website name, Cloudfront offers a certificate valid only for its own domain cloudfront.net.
Then, you need a couple of records in your DNS zone in Route53:
- The public website records ("www" and apex) should point to your Cloudfront distribution.
- Another auxiliary record ( for example, "origin") is required for your Cloudfront origin setup, as Cloudfront does not allow IP origins. This auxiliary record should point to your actual web server. It is important that you configure your distribution to forward the Host header in order to allow your web server to serve the right contents.
With this setup, your customers are routed to Cloudfront through your www/apex records, and Cloudfront locates the actual web server through the origin record. Clodflare saves you from implementing all this logic because you delegate your DNS to them, but in fact this is what they do in the background when you enable the CDN feature in your DNS panel.
Best Answer
CloudFront doesn't natively support this.
The original request path is forwarded intact to the origin server, with only one exception: if the origin has an Origin Path configured, that value is added to the beginning of the path before the request is sent to the origin (and, of course, this doesn't help, here).
However, you can modify the path during CloudFront processing using a Lambda@Edge Origin Request trigger.
This type of trigger fires only on cache misses -- after the CloudFront cache is checked but before the request is sent to the origin -- and allows modification of the request that will be sent to the origin... including the path. The response from the origin, if cacheable, is stored in CloudFront under the path originally requested by the browser (not the modified path), so caching still works correctly even when the trigger modifies the path.
The trigger code to remove the first level from the path might look something like this:
There's more discussion of this in my answer to a very similar question at Stack Overflow, which is the original source of the code snippet above. It was originally written in Node.js 6.10 but is still compatible with the Lambda@Edge interface, which now uses newer versions of Node. Alternately, if you prefer, the trigger code can also be written in Python.
You might also need to arrange for
/subfolder
to be redirected to/subfolder/
so that paths relative to the root of the second origin are correctly canonicalized.