# Solving CloudFront Path Routing Issues for Static Websites Hosted on S3

## Introduction

Recently, at work, I encountered the following scenario:

A static website was uploaded to an S3 bucket with a filepath similar to the following:

```markdown
- 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](http://www.just-an-example.xyz/),

So,

Visiting [www.just-an-example.xyz](http://www.just-an-example.xyz/) should serve the root index.html, Going to [www.just-an-example.xyz/page1](http://www.just-an-example.xyz/) should serve the page1/index.html.

The problem arose when [www.just-an-example.xyz/page1/index.html](http://www.just-an-example.xyz/) was working fine, but [www.just-an-example.xyz/page1](http://www.just-an-example.xyz/) 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.

## **Step 1: Enable Static Website Hosting for S3 Bucket**

Navigate to the S3 bucket properties page and ensure static website hosting is enabled if not done already.

![Enabling static website for s3 bucket](https://cdn.hashnode.com/res/hashnode/image/upload/v1700565946014/f18c1cd2-72e8-4ed0-9823-bb995a6f9d47.png align="center")

## **Step 2: Configure Bucket Permissions**

Set up the bucket policy with the following permissions:

```json
{
    "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.

## **Step 3: Create Lambda Function**

```javascript
'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](http://www.just-an-example.xyz/pag1) should serve [www.just-an-example.xyz/page1/index.html](http://www.just-an-example.xyz/pag1)

Replace `<###>` with your S3 bucket's static URL. Deploy the Lambda function and publish a new version.

## **Step 4: Update Lambda Role Permissions**

In the Lambda configuration tab, under permissions, click on the role name link.

![Finding lambda function's role](https://cdn.hashnode.com/res/hashnode/image/upload/v1700566060143/c938af19-381c-4369-ab48-c88afcc3d07e.png align="center")

Update the trust relationships to match the following:

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "lambda.amazonaws.com",
                    "edgelambda.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

## **Step 5: Update CloudFront Configuration**

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.

![Cache settings in the behavior tab of cloudfront distribution](https://cdn.hashnode.com/res/hashnode/image/upload/v1700566166945/33f1ef38-a310-4448-81ed-28f88b3428d4.png align="center")

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

![Associating lambda function with the cloudfront distribution](https://cdn.hashnode.com/res/hashnode/image/upload/v1700566217024/6decd83f-e368-41db-b4ce-5a7cbc509bc8.png align="center")

Finally, invalidate the files if needed.

Phew, with these steps, the issue should be resolved.

[  
](https://medium.com/tag/aws?source=post_page-----4b5a99a96a44---------------aws-----------------)
