Lionel Martin


How to host a Single Page Application with AWS CloudFront and Lambda@Edge

Configuring CloudFront to support push-state URLs

This one seems like an easy one, however I’m appalled by how much time can be wasted configuring AWS for static website hosting and there must be a better way:

serverless: we will be hosting static assets so no messing around with servers for that
 — cheap and ultra-performant: by leveraging AWS CDN, we’ll get world-class global distribution and caching for pennies
 — automated: this is not a tutorial of the AWS console. Some of the marketing agencies I consult for would typically deploy new landing pages or static websites every week. If that’s something you do regularly too, it is worth investing an hour into automating this into a push-button procedure isn’t it?

About automating

There is something very powerful about automating even the little things. The benefits are much more than the sum of time savings over time, it will reduce human errors, improve the reliability of your service, get your employees busy with higher-value work and boost employee morale by eliminating boring low-value tasks. It improves your business globally.

A game changer: with Lambda@Edge, we can now configure CloudFront to support push-state URLs

The problem so far with hosting an SPA on CloudFront is that we couldn’t support push-state URLs because CloudFront doesn’t offer a “catch-all” configuration i.e. we couldn’t serve the same index.html file for any request to the domain. The solution was to use hash-state “ugly” urls which was a turn-off for most.

In 2017, AWS released Lambda@Edge, a service that runs your Lambda functions “at the edge” of the cloud i.e immediately before/after the CDN queries its cache:

  • viewer request: after CloudFront receives a request from a viewer
  • origin request: before CloudFront forwards the request to the origin
  • origin response: after CloudFront receives the response from the origin
  • viewer response: before CloudFront forwards the response to the viewer

This will let us rewrite the requests on origin request so that we always serve index.html for all push-state routes requests. Happy days!

Our origin-request Lambda@Edge function

What do I need?

  • an existing domain name registered as a hosted zone in your AWS account
  • a wildcard SSL certificate for all subdomains
  • terraform and the aws CLI installed and configured on your machine
  • a dummy React or Vue application to deploy

Show me the Terraform module!

I’ve released a Terraform module that does all the heavy lifting for you:

Copy the example files into your SPA root folder: 


This will pull the Terraform module from the Terraform registry

Replace the values in terraform.tfvars with your aws CLI profile name, your app name and your domain name. Make sure you have an existing certificate for * in the us-east-1 region.

Run terraform init then terraform apply and wait for the S3 bucket, Lambda function and CloudFront distribution to be created:

You can now compile your front-end assets locally and push them directly to the S3 bucket to deploy your application:

yarn build && aws s3 cp --recursive --acl=public-read build/ s3://$(terraform output s3_bucket)

An alternative is to use the CodeCommit Git repository and CodePipeline pipeline that has been created by the Terraform module to let AWS build your application, run your tests and deploy on S3.

Either way, your application is now running on CloudFront!

The push-state URL working properly when refreshing the page

Lionel is Chief Technology Officer of London-based startup Wi5 and author of the Future-Proof Engineering Culture course. You can reach out to him on

More by Lionel Martin

Topics of interest

More Related Stories