Solving CloudFront Path Routing Issues for Static Websites Hosted on S3

Search for a command to run...

No comments yet. Be the first to comment.
Now that we have our database set up and seeded in part 4, it's crucial to grasp some key concepts of Fastify before diving into application development. You can find the complete code for this part here Fastify Concepts Lifecycle and Hooks Fastify o...

Continuing from part 3, we will explore how to seed data using Knex. To follow along, you can use the part-3 branch from this repo. The full code for this post is in the part-4 branch. Power of seeding Seed files allow us to populate the database wit...

Continuing from Part 2, our next step is to set up the database for our application. To follow along, you can clone this branch. The complete code for this part can be found here. To keep things straightforward for our purpose, we'll create two table...

In part 1, we have already set up our project. In this part, we will familiarize ourselves with the basic concepts of Fastify, which are essential for creating REST APIs. If you wish to follow along, you can clone the Part I branch. For the complete ...

Recently, at work, I encountered the following scenario:
A static website was uploaded to an S3 bucket with a filepath similar to the following:
- index.html
- page1
- index.html
- page2
- index.html
This S3 bucket file was served through CloudFront.
The use case was simple:
Let’s assume the CloudFront URL was www.just-an-example.xyz,
So,
Visiting www.just-an-example.xyz should serve the root index.html, Going to www.just-an-example.xyz/page1 should serve the page1/index.html.
The problem arose when www.just-an-example.xyz/page1/index.html was working fine, but www.just-an-example.xyz/page1 wasn’t.
It sounds simple, but the solution wasn’t as straightforward as I thought.
This post provides the solution to the above issue. It’s written for someone else who might face this problem and for my future self, who will no doubt forget about this and find myself scratching my head in the future. This assumes you are familiar with AWS services such as buckets, IAM policies, and CloudFront.
Navigate to the S3 bucket properties page and ensure static website hosting is enabled if not done already.

Set up the bucket policy with the following permissions:
{
"Version": "2008-10-17",
"Id": "PolicyForCloudFrontPrivateContent",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipal",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<YOUR_BUCKET_NAME>/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::<ACCOUNT_NUMBER>:distribution/<CLOUDFRONT_DISTRIBUTION_ID>"
}
}
},
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<YOUR_BUCKET_NAME>/*"
}
]
}
Also, enable public access to the bucket.
'use strict';
exports.handler = (event, context, callback) => {
let request = event.Records[0].cf.request;
let olduri = request.uri;
if (olduri.indexOf(".") === -1) {
olduri = olduri + '\/';
}
olduri = olduri.replace(/\/\//, '\/');
request.uri = olduri.replace(/\/$/, '\/index.html');
request.headers['host'] = [{ key: 'host', value: '<###>-east-1.amazonaws.com'}];
// Return to CloudFront
return callback(null, request);
};
We are basically telling any request to www.just-an-example.xyz/page1 should serve www.just-an-example.xyz/page1/index.html
Replace <###> with your S3 bucket's static URL. Deploy the Lambda function and publish a new version.
In the Lambda configuration tab, under permissions, click on the role name link.

Update the trust relationships to match the following:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com",
"edgelambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
Change the origin to point to the static S3 bucket URL created above from the origins tab.
Update the behavior config in the behavior tab:
For cache settings, choose legacy cache settings and include the host header. Reference the image below for clarity.

Finally, in the function associations, update the origin request to point to the previous Lambda function ARN along with the version number.

Finally, invalidate the files if needed.
Phew, with these steps, the issue should be resolved.