Dynamically rewrite HTTP headers
Use the new CloudFront functions for common use-cases
When hosting your static website on Amazon Web Services, you commonly expose this content through AWS CloudFront. Up until recently, dynamically modifying responses required setting up Lambda@Edge functions.
We've written about this setup in a previous blog post.
On the one hand, these Lambda functions are very powerful and can be written in many languages, benefiting from the full AWS Lambda stack. On the other hand, however, in a lot of cases they are also too complex and require too many unnecessary steps to deploy and maintain. It is also not always easy to check which version of the Lambda function is used for which CloudFront distribution.
CloudFront functions
Earlier this month, AWS introduced CloudFront functions, which are limited in power, but easier to write and also present you with a clear overview which distribution is using them.
Another important difference is the pricing: CloudFront functions only cost $0.10 per million requests, where Lambda@Edge would cost $0.60.
The functions are written using plain JavaScript with ES 5.1 language features and some additional features from ES 6 and up, like arrow functions and string templates.
Use Cases
A couple of common cases arise when hosting static websites:
- Add necessary security headers to prevent frame hijacking, content sniffing and insecure connections
- Add or modify caching headers if they can not be set in the static content objects' metadata
- Redirect or rewrite parts of the URL
Examples
Each function is called with an event that contains the following properties:
- context, with information about the distribution and the event type
- viewer, containing info about the client IP address
- request, with uri, method, headers, query string parameters and cookies for the request
- response, when called upon a content response. It contains status, headers and cookies
Let's add some examples for all 3 use cases mentioned above.
Add security headers
function handler(event) {
// Get contents of response
var response = event.response;
var headers = response.headers;
// Set new headers
headers['x-ua-compatible'] = {value: 'IE=edge,chrome=1'};
headers['strict-transport-security'] = {value: 'max-age=63072000'};
headers['x-content-type-options'] = {value: 'nosniff'};
headers['x-xss-protection'] = {value: '1; mode=block'};
headers['x-frame-options'] = {value: 'SAMEORIGIN'};
headers['content-security-policy'] = {value: "frame-ancestors 'self'"};
// Return modified response
return response;
}
We can now test this function before publishing it:
And we can see the security headers are added:
Now, associate this function with the Viewer Response of one or more distributions:
This screen also gives you a quick overview, at all times, of the distributions this function is associated with.
Rewrite caching headers
Upon a response with static content, we still have the possibility to rewrite headers based on the request that this response is for:
function handler(event) {
//Get contents of response
var request = event.request;
var response = event.response;
var headers = response.headers;
//Set new headers
if (request.uri.endsWith('.xml') || request.uri.endsWith('.json')) {
headers['cache-control'] = {value: 'public, max-age=0, must-revalidate'};
}
//Return modified response
return response;
}
Redirect URIs with duplicate slashes
This function is added in the Viewer Request handling of a distribution, so it can generate a response before even hitting the static content:
function handler(event) {
//Get contents of response
var request = event.request;
var cleanUri = request.uri.replace(/\/+/g,'/')
//Return modified request
if (cleanUri !== request.uri) {
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: {
'location': { value: cleanUri }
}
};
} else {
return request;
}
}
Conclusion
The examples above clearly show that writing these functions is pretty simple. If your current setup is already employing Lambda@Edge, replacing those with the new CloudFront functions should be pretty straight-forward. For detailed information and more examples, check out the CloudFront documentation.
And as always, feel free to contact us if you have any question, suggestion or correction to this post. Get in touch via our Support Channel.