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!
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 theaws
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: wget https://raw.githubusercontent.com/li0nel/terraform-aws-single-page-application/master/examples/main.tf
wget https://raw.githubusercontent.com/li0nel/terraform-aws-single-page-application/master/examples/outputs.tf
wget https://raw.githubusercontent.com/li0nel/terraform-aws-single-page-application/master/examples/terraform.tfvarswget https://raw.githubusercontent.com/li0nel/terraform-aws-single-page-application/master/examples/variables.tf
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 *.cdn.mydomain.com
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!
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 https://getlionel.com