Client-side routing with Gatsby, AWS CloudFront and Lambda@Edge

In this article I want to touch on the nuances associated with client-side routing when deploying a Gatsby site behind AWS CloudFront.

CloudFront is a great choice for serving your static site not only for improving content delivery (through its network of Edge locations) but also because it allows you protect your S3 bucket content using Origin Access Identity (OAI), so its well worth the additional setup.

Automatic redirects

Once your bucket is configured as a static website AWS S3 automatically handles request redirects to the websites index document where request handling can be done by the client-side router function. Either of the following requests will result in S3 serving the index document.

http://.s3-website..amazonaws.com/ http://.s3-website..amazonaws.com

You can configure a CloudFront distribution to sit in front of your S3 bucket. The distribution configuration specifies the S3 buckets index.html file as the default root object to return when a viewer request points to the root URL. At the outset the single page application appears to work fine. The root page loads and the client side router function handles navigation requests. However when we bypass the router function and attempt to reload an internal page on our site through CloudFront, for example reloading the URL

https://.cloudfront.net/internalPage

then CloudFront will return a 403 forbidden error because it can’t find a file named internalPage in our S3 bucket. When we attempt the same request but using the S3 buckets URL the request succeeds which leads us to our solution.

The redirect behavior we need to replicate

When S3 receives a request similar to the example above (with context path /internalPage) it will look for an object named internalPage in the bucket which is marked as a static website. If no object exists S3 will search for an index document, internalPage/index.html. If the document is located then S3 returns a 302 found message and points to internalPage/ ( note the forward slash). For all subsequent requests to internalPage/ S3 will return internalPage/index.html.

We need to replicate this behavior in our CloudFront distribution to get Gatsby working.

Lambda@Edge to the rescue

Lambda@Edge gives you a means to change CloudFront requests and responses at different points. In the case we will modify the request before it is sent to the origin (S3).

At the time of writing this article it was only possible deploy Lambda functions which were created in the us-east-1 region to a CloudFront edge location.

The handler logic is straight forward. If the request path does not explicitly point to the index document then we append to the request and send to S3.

Associating a Lambda function with your CloudFront distribution can be done in a number of ways. In this example I’m simply using the AWS Console and deploying via the Lambda function page and the Actions drop down. As mentioned previously the option to deploy to an edge location is currently limited to functions deployed in the us-east-1 region.

Associations are created using the distributions ARN.

The function deployment to Edge will update your distributions behavior settings and include an association with the Lambda function.

Once CloudFront finishes updating you should be all set and can begin validating request behavior.

Conclusion

This was just a quick run through of the configuration needed to get your Gatsby site working with CloudFront. If there is anything you would do differently please let me know in the comments below.

Resources

Lambda@Edge — https://docs.aws.amazon.com/lambda/latest/dg/lambda-edge.html

Configuring S3 index document — https://docs.aws.amazon.com/AmazonS3/latest/dev/IndexDocumentSupport.html

Tech Lead | AWS Community Builder | AWS Solutions Architect Pro | Passionate about learning | Languages include Java, Go, Typescript, and more recently Rust