Serve S3 Content from CloudFront with URLs Rewrite using Lambda@Edge in AWS

Serve S3 Content from CloudFront with URLs Rewrite using Lambda@Edge in AWS

How to Serve S3 Content from CloudFront Using Lambda@Edge and Rewrite URLs in AWS

AWS CloudFront is a powerful Content Delivery Network (CDN) service that helps you distribute content globally with low latency and high transfer speeds. One common use case is serving static content from Amazon S3 buckets through CloudFront. However, there are situations where you may want to rewrite the request URL to match the structure of your S3 buckets — for instance, serving content from /maps/* but stripping the /maps prefix from the request URL.

In this tutorial, we’ll show you how to use Lambda@Edge to modify the request URI before it reaches your S3 bucket. This allows you to serve S3 content under different URL structures, such as serving content from s3://bucketB/images/01.jpg when the request is made to https://example.com/maps/images/01.jpg.

Use Case Scenario:

Let’s consider the following scenario:

  • Bucket A is the default origin for CloudFront and serves static assets for requests to *.
  • Bucket B serves content under the path /maps/*.
  • You want to modify requests such that URLs like https://example.com/maps/images/01.jpg map to s3://bucketB/images/01.jpg — essentially removing the /maps prefix from the request path when routing to Bucket B.

In this article, we will walk through how to accomplish this using Lambda@Edge.

Step 1: Setting up AWS CloudFront and S3

First, ensure that your CloudFront distribution is correctly configured to serve content from two S3 buckets:

  • Bucket A: This bucket will handle default requests (i.e., requests to paths like /*).
  • Bucket B: This bucket will handle requests that match /maps/*.

To set this up in CloudFront:

  1. Create or use an existing CloudFront distribution.
  2. Add two cache behaviors:
    • The first behavior will route all requests matching the pattern /maps/* to Bucket B.
    • The second behavior will route all other requests to Bucket A.

Step 2: Create the Lambda@Edge Function

Next, we’ll write a Lambda function that rewrites the request URL by removing the /maps/ prefix before it reaches Bucket B. This function will be triggered at the Origin Request stage of CloudFront, ensuring the URL modification happens just before CloudFront forwards the request to the origin (i.e., your S3 bucket).

Use a blueprint Modify HTTP response header as the template function with Node.js 18.x runtime.

Important
Make sure that you’re in the US-East-1 (N. Virginia) AWS Region (us-east-1). You must be in this Region to create Lambda@Edge functions.

Here’s the code for the Lambda function:

1
export const handler = async (event) => {
2
const request = event.Records[0].cf.request;
3
const uri = request.uri;
4
5
// Log the incoming request URI for debugging purposes
6
console.log('Incoming URI:', uri);
7
8
// Check if the URI starts with "/maps/" and modify it if necessary
9
if (uri.startsWith('/maps/')) {
10
// Remove "/maps" from the beginning of the URI
11
const modifiedUri = uri.substring(5); // Removing '/maps' (which is 5 characters long)
12
console.log('Modified URI:', modifiedUri);
13
14
request.uri = modifiedUri;
15
}
16
17
// Return the modified request to CloudFront
18
return request;
19
};

This function:

  • Logs the incoming request URI for debugging purposes.
  • Checks if the URI starts with /maps/ and removes the /maps prefix if found.
  • Returns the modified request so CloudFront can forward it to Bucket B.

Step 3: Deploy Lambda@Edge

After creating the Lambda function and IAM role, deploy the Lambda function to Lambda@Edge by following these steps:

  1. Open the Lambda Console.
  2. Select the function you created and click Actions > Deploy to Lambda@Edge.
  3. Choose the CloudFront distribution, set Cache behavior to Cloudfront behaviour /maps/* and set the CloudFront event to Origin Request, leave Include body unchecked.
  4. Click Deploy.

Step 4: Testing

Now, let’s test it:

  1. Request: https://example.com/maps/images/01.jpg
  2. The Lambda function modifies the request to: /images/01.jpg
  3. CloudFront forwards the modified request to Bucket B, and the content is served from s3://bucketB/images/01.jpg.

Step 5: Monitor and Debug with CloudWatch Logs

You can monitor the Lambda function’s performance and debug any issues using CloudWatch Logs. Each time the function runs, it logs the incoming URI, the modified URI, and other relevant information.

You can enable CloudFront access logs to track requests and verify that the URI is correctly modified by the Lambda function.

Here’s an example of the logs you might see in CloudWatch:

1
START RequestId: abc123xyz
2
INFO Incoming URI: /maps/images/01.jpg
3
INFO Modified URI: /images/01.jpg
4
END RequestId: abc123xyz

Conclusion

By using Lambda@Edge, we’ve successfully configured CloudFront to serve content from Bucket B while rewriting the URL to remove the /maps/ prefix. This provides a clean and flexible URL structure for serving static content, such as images or other assets, from S3 via CloudFront.

Lambda@Edge offers a powerful way to perform serverless computation at the edge of the AWS network, enabling real-time modification of requests and responses — a great tool for enhancing the performance and flexibility of your AWS infrastructure.

Further Reading