Sitemap
FAUN.dev() 🐾

We help developers learn and grow by keeping them up with what matters. šŸ‘‰ www.faun.dev

About AWS Suggested way to host Static Web Sites (JAM Stack or SPA)

--

Quick discussion for the best practice of hosing Static Web Site on AWS

Press enter or click to view image in full size

Recently, I have seen more and more our new customers move from traditional App architect to new types of app designs.

The traditional way is to use EC2, ALB, and ASG (Auto Scaling Groups) to host the applications. Nowadays, most new web app projects are using SPA frameworks (React, Angular, Vue, etc) for frontend and put their API logic either to lambda/API-gateway (serverless) or Containers (ECS or EKS)

This type of transition is not new. Many companies like Heroku and Netlify have already done some great work to solve the problem. Interesting enough, AWS still doesn’t provide an easy solution to compete

AWS S3 has a simple Static Web Site option. If you search Medium, you will find many articles are still suggesting that way. That solution is not suggested by AWS for ā€˜real projects’, the biggest show stoppers are:

  • It only supports HTTP but not https
  • The bucket has to be ā€˜Public Read’, that means you can’t put WAF (Web App Firewall) in front of it to protect your production site from DDOS attack or provide rate limit control

AWS recently published a ā€œsuggested wayā€ of hosting Web Site using Cloudfront, S3, and Lambda@Edge. You can find the details here

The solution is quite like what we did for some of our customers, glad it is now suggested by AWS as their best practice.

But the solution provided in the video has some issues and they didn’t mentioned. I will list them here and provide my work-around in real-life projects, so hopefully, you will find them helpful.

So the issues the solution above trying to solve are:

  • Host all the frontend files in a private S3, it can only be accessed by Cloudfront.
  • To allow only Cloudfront to access it, we set up OAI (Origin Access Id) in Cloudfront and use a permission policy like below to only access Cloudfront to access our S3 bucket.
Press enter or click to view image in full size
Private S3 Bucket Policy
  • Use lambda@edge to handle ā€˜requests’ and ā€˜response’. In requests handler lambda, check if the URL is a directory (ends with ā€œ/ā€) or it doesn’t contain ā€œ.ā€ in it which means it’s not trying to load a static file (.html, .js or .css etc) then send a redirect (302) back to redirect client to /index.html
exports.handler = async (event) => {
const request = event.Records[0].cf.request;
const uri = request.uri;
if (uri.endsWith('/')) {
request.uri += 'index.html';
}else if { !uri.includes('.')){
request.uri += '/index.html';
}
}

If you search Medium, you will probably find a lot of articles talking about hosting websites using Cloudfront + S3 with a Cloudfront feature called ā€˜custom error’. That seems to be much simpler than this lambda@edge solution

The biggest problem of using ā€˜Cloudfront Custom Error’ showing in the article above is your web app might be like this below:

  • Frontend is on S3 but API backend is also using the same domain with different URL paths. For example, your S3 part is like ā€œexample.comā€ and your API is ā€œexample.com/apiā€.
    If you simply set ā€œCustom Errorā€ redirect for all 404 and 403 to ā€œindex.htmlā€ like above, your API’s ā€œexample.com/apiā€ calls might also send back 404 or 403 response (indicate your request not found or not allowed). Those responses will also be redirected to /index.html which will break your app
  • Because the ā€œCloud from Custom Errorā€ is for the Distribution level only but not for ā€œBehavioursā€ so you can’t specify that the /API path behavior should bypass the redirection to /index.html

So if your project is like what I mentioned above. Don’t use the ā€œCustom Errorā€ feature, it will come back and bite you

Get Tingli Tan’s stories inĀ yourĀ inbox

Join Medium for free to get updates fromĀ thisĀ writer.

There are some situations the AWS Video didn’t cover:

  • What about hosting more than one app on the same S3 bucket. For example, one of my clients is using / for the main app, ā€œ/adminā€ for another Angular app to manage settings, and ā€œ/reportsā€ is another SPA app to do reporting.
  • The lambda they suggested above only checks if the URL has ā€œ.ā€ in it or not. It’s not really proper to cover all the cases, a customer might have files to serve that doesn’t have ā€œ.ā€ in the URL

Here is the updated Lambda function to handle the ā€˜response’

The logic is that when the URL path is not /API then it will check if it’s for /admin or /reports then redirect to their specific /admin/index.html or /reports/index.html

As AWS Video mentioned, lamba@edge is applied to ā€˜Behaviour’ level, so your /API or anything that is not from your S3 Origin will not be impacted by the 404/403 redirection. That is exactly what we want

Before we end the discussion. Here are the code snippets to deploy lambda@edge using Terraform. Just to save your time looking for it :)

Hopefully, you now have many options to deploy your web apps and know the pro and cons of each of them. At least the lambda@edge one is what AWS suggested.

I think AWS will provide more tools in the future to make these solutions simpler, at least the AWS Amplify is trying, but it has its own issues too

I hope this helps and thanks for reading.

Press enter or click to view image in full size

Follow us on Twitter 🐦 and Facebook šŸ‘„ and Instagram šŸ“· and join our Facebook and Linkedin Groups šŸ’¬.

To join our community Slack team chat šŸ—£ļø read our weekly Faun topics šŸ—žļø, and connect with the community šŸ“£ click here⬇

If this post was helpful, please click the clap šŸ‘ button below a few times to show your support for the author! ⬇

--

--

FAUN.dev() 🐾
FAUN.dev() 🐾

Published in FAUN.dev() 🐾

We help developers learn and grow by keeping them up with what matters. šŸ‘‰ www.faun.dev

Tingli Tan
Tingli Tan

Written by Tingli Tan

Principal Technology Architect at TELUS