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
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.
- 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.
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ā¬











