Need a fast, globally accessible website without managing servers? AWS S3 + CloudFront has you covered. In fact, for static websites, AWS S3 and CloudFront offer one of the most reliable, cost-effective ways to get your site into production without the headaches of traditional server(e.g. EC2 compute) management.
In this guide, I will walk you through exactly how I deploy static website or a front-end app using this powerful combo, sharing all the tips and gotchas I’ve discovered along the way. This isn’t just theory—it’s battle-tested knowledge from someone who’s fixed deployment pipelines at 2 AM more times than I care to admit.
When I started my career, we deployed everything to the same servers. Frontend, backend, database—it all lived together in a tangled mess. Now I absolutely cringe thinking about those days! 😱
So, why go this route? S3 is Amazon’s rock-solid storage service—scalable, cheap, and zero maintenance. CloudFront, their CDN, takes your site and blasts it across the globe for lightning-fast access. I love how this combo keeps things dead simple while packing a punch.
Here’s the real kicker: it forces a clean split between front-end and back-end. Your front-end lives happily on S3 and CloudFront, totally independent from whatever your back-end’s up to. No server to manage means no downtime—your site’s always up, even if the back-end crashes. For front-end-heavy apps, it might even keep chugging along, serving friendly messages like “Hey, our servers are napping, hang tight!” That’s a win in my book.
Using S3 and CloudFront for your front-end deployment brings several game-changing benefits:
💡ProTip: Are you new to AWS and not sure how to get a handle on it? Consider our Roadmap for Learning AWS for a beginner-friendly curriculum.
Here’s how the set-up looks like from a high-level architecture diagram:
Let’s jump right into the practical steps. First, we need to configure an S3 bucket to host our front-end files.
Note: We will be using cli commands in this article. If you don’t have AWS CLI set up on your local machine, first go over the official documentation to set it up. Alternatively, you can do most of these steps manually through the AWS console if you prefer.
I always name my buckets to match the domain name I’ll be using. It keeps things organized and makes troubleshooting easier.
aws s3 mb s3://your-awesome-website.comBashThis step transforms a regular file storage bucket into a proper web server:
aws s3 website s3://your-awesome-website.com \
--index-document index.html \
--error-document error.htmlBashNeed to disable “Block All public access” settings:
aws s3api put-public-access-block \
--bucket your-awesome-website.com \
--public-access-block-configuration "BlockPublicAcls=false"BashThis is crucial – Your bucket needs a policy that allows public read access:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "s3:GetObject",
"Effect": "Allow",
"Principal": "*",
"Resource": "arn:aws:s3:::your-awesome-website.com/*",
"Sid": "PublicReadGetObject"
}
]
}JSONApply it with:
aws s3api put-bucket-policy \
--bucket your-awesome-website.com \
--policy file://bucket-policy.jsonBashNow, we can upload our application files. I typically build my React/Vue/Angular app locally first:
# Build your application
npm run build
# Upload to S3
aws s3 sync ./dist/ s3://your-awesome-website.com/ --deleteBashThe --delete flag is important—it removes old files that aren’t in your new build. I once skipped this flag and couldn’t figure out why users were seeing a mix of old and new versions of my site!
At this point, your site should be accessible via the S3 website endpoint (something like http://your-awesome-website.com.s3-website-us-east-1.amazonaws.com).
S3 static website hosting is great, but CloudFront takes your deployment to another level. Here’s why I always use it:
aws cloudfront create-distribution \
--origin-domain-name your-awesome-website.com.s3-website-us-east-1.amazonaws.com \
--default-root-object index.htmlBashThis command is simplified—in practice, you’ll want to configure additional settings through the AWS console or a more detailed CLI command.
Here’s a gotcha I discovered the hard way: When you create the distribution, make sure you’re using the S3 website endpoint (ends with .s3-website-region.amazonaws.com) and NOT the regular S3 endpoint. The website endpoint handles redirects and error pages correctly!
CloudFront’s default cache settings are good, but I typically customize them for front-end apps:
I always connect my CloudFront distributions to custom domains with HTTPS. The AWS CLI steps are a bit verbose, so I typically do this through the console.
⚠️ Pro Tip: The domain typically takes 15-30 minutes to propagate, but I’ve seen it take up to 24 hours in some regions!
Manual deployment gets tedious and fast. I automate everything with GitHub Actions now—here’s a simplified workflow file I use:
name: Deploy Static Site With S3 and CloudFront
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy to S3
run: |
if [ "${BRANCH_NAME}" == "main" ]; then
aws s3 sync ./ s3://your-awesome-website.com/ --delete
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }}
--paths "/*"
fi
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_DEFAULT_REGION: 'us-east-1'
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}YAMLA critical step I always include is the CloudFront invalidation. Without it, users might continue seeing cached versions of your site for hours or days after deployment!
If you’re deploying a Single-Page Application (React, Vue, Angular, etc.), you need to handle client-side routing. I’ve encountered this problem many times, especially when users bookmark deep links.
The solution is to configure CloudFront to return your index.html file for any 404 errors. This way, when a user visits a deep link like yourdomain.com/products/123, CloudFront serves index.html, and your client-side router takes over.
Create a custom error response in your CloudFront distribution:
I’ve experienced these issues often enough to list them here:
This setup is fantastic but comes with some limitations:
Once you’ve mastered the basics, consider these advanced techniques:
After years of deploying static websites and front-end apps to various platforms, I’m convinced that S3 and CloudFront offer the best combination of simplicity, performance, and cost-effectiveness for most applications.
What deployment strategy are you currently using for your front-end applications? Have you encountered any unique challenges with S3 and CloudFront that I haven’t covered? I’d love to hear about your experiences! Happy building 🛠️!
Because CloudFront acts as a global CDN cache. It drastically reduces load times by serving your S3 content from servers closest to users and provides HTTPS out-of-the-box for custom domains
Simply replace files in the S3 bucket (manually or via aws s3 sync). Then run a CloudFront invalidation to purge cache for changed files. Our guide’s automation section shows how to streamline this in one go.
Yes – use Origin Access Control (OAC). Our steps use CloudFront OAC so the S3 bucket isn’t public. This way, viewers can only fetch content via CloudFront, keeping your bucket URL locked down.
Very little for most small sites. S3 storage is pennies per GB; CloudFront charges per GB delivered + requests. For a personal or small business site, costs often stay within the AWS Free Tier or just a few dollars per month. (Example: one Medium article reported ~$0.93/month for a low-traffic site)
Tired of repetitive tasks eating up your time? Python can help you automate the boring stuff — from organizing files to scraping websites and sending…
Learn python file handling from scratch! This comprehensive guide walks you through reading, writing, and managing files in Python with real-world examples, troubleshooting tips, and…
You've conquered the service worker lifecycle, mastered caching strategies, and explored advanced features. Now it's time to lock down your implementation with battle-tested service worker…
This website uses cookies.