Reputation: 111
I have an Angular app's static files being served on an S3 bucket through Cloudfront. My Cloudfront distribution has error pages set up so it still renders the Angular's index.html. This means that if I request <cloudfront-distribution>.cloudfront.net/home-page
, instead of saying that it didn't find a file named home-page
on the S3 bucket, it will still render the angular app and the angular app will handle that /home-page
route.
I needed to include some security headers on the app server so I set up a Lambda@Edge function to inject those headers on a viewer response event (like described here https://aws.amazon.com/blogs/networking-and-content-delivery/adding-http-security-headers-using-lambdaedge-and-amazon-cloudfront/).
The Lambda@Edge is working for routes that actually correspond to a file in the S3 bucket (if I have a file called image.png
on the root folder of my S3 bucket, and I request <cloudfront-distribution>.cloudfront.net/image.png
, I see the response headers that I injected via the Lambda@Edge function. The issue is when accessing a route that doesn't correspond to a file in the S3 bucket. If I access <cloudfront-distribution>.cloudfront.net/home-page
, S3 will return a 404, Cloudfront will handle the 404 and act accordingly to the Error Pages configuration, i.e., respond with a 200 status code and render the index.html file. And when this happens, I don't see any of the headers I injected via the Lambda@Edge function, while all the other script files of my Angular app have the headers.
How can I make all responses go throught the Lambda@Edge function?
Upvotes: 11
Views: 4329
Reputation: 2618
Based on the answer of @philarmour I will show a concrete example for a solution.
Remove the custom 404 page that returns /index.html
in your CloudFront Distribution. Then add a new CloudFront Function (customize the code below to your needs)
var rootPaths = {
css: 1,
favicon: 1,
fonts: 1,
img: 1,
js: 1,
'robots.txt': 1,
};
function handler(event) {
var firstPart = event.request.uri.split('/')[1];
if (!(firstPart in rootPaths)) event.request.uri = '/index.html';
return event.request;
}
Associate the function with your CloudFront Distribution on VIEWER_REQUEST
and don't forget to publish it.
Upvotes: 2
Reputation: 414
I just went through this exact same situation. I found this page in the AWS docs to be most helpful: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-cloudfront-trigger-events.html
While the current answer is definitely correct for this question, I had another constraint that might be helpful to discuss:
As you observed (and called in the docs) the viewer response event are not trigger when there is as error at the origin - including when a custom error page is returned as a 200.
Option #1 - Swapping to the Origin Request event
If you're already using Lambda@Edge and don't want to change anything this is probably the simplest change.
Option #2 - Stop Using Custom Error Responses
For me, I went this route. I needed to create a second function for the Viewer Request event that re-writes any requests that are not specifically for an s3 resource so React Router paths return my index.html. (Something like this: https://stackoverflow.com/a/60012469/4413888). finally, removed my existing custom error responses in CloudFront.
Upvotes: 7
Reputation: 1
It works fine after running Lambda@Edge Function on Origin Response. However do not forget to run invalidation on CloudFront after making this change. Also it would save money as Lambda would only be executed when request would be sent to Origin
Upvotes: 0