
The process doesn’t have to be complicated if you need to deploy a static website or an FE app. In fact, 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.
❓Why S3 and CloudFront for Static Site Deployment?
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:
- Complete separation of concerns – Your front end and back end can evolve independently. Trust me, this is liberating! I’ve worked on projects where backend upgrades used to take down the entire site. Not anymore.
- Near 100% uptime – CloudFront serves cached content from edge locations worldwide, so your site stays up even during S3 maintenance. I’ve seen frontends remain operational during complete backend outages, providing users with helpful error states instead of broken pages.
- Blazing fast performance – CloudFront’s global CDN network delivers your assets from the location closest to your users. The performance difference is remarkable—I’ve seen load times drop from seconds to milliseconds.
- Ridiculously cost-effective—You’re paying pennies for what would cost hundreds on traditional hosting.
- Scales automatically like crazy – The infrastructure handles it without breaking a sweat, whether you have 10 visitors or 10 million.
💡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.
🤩 Getting Started: Setting Up S3 for Static Website Hosting
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.
Step 1: Create an S3 Bucket
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.com
Code language: JavaScript (javascript)
Step 2: Configure the Bucket for Website Hosting
This 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.html
Code language: JavaScript (javascript)
Step 3: Set the Bucket Policy for Public Access
Need to disable “Block All public access” settings:
aws s3api put-public-access-block --bucket your-awesome-website.com --public-access-block-configuration "BlockPublicAcls=false"
Code language: PHP (php)
This 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"
}
]
}
Code language: JSON / JSON with Comments (json)
Apply it with:
aws s3api put-bucket-policy --bucket your-awesome-website.com --policy file://bucket-policy.json
Code language: JavaScript (javascript)
Step 4: Upload Your Front End Files
Now, 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/ --delete
Code language: PHP (php)
The --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
).
🚀 Supercharging Performance with CloudFront
S3 website hosting is great, but CloudFront takes your deployment to another level. Here’s why I always use it:
- HTTPS support – S3 websites don’t support HTTPS out of the box, but CloudFront does
- Global distribution – Your site loads faster worldwide
- Better caching – Reduced latency and lower S3 costs
- Additional security – WAF integration and other protections
Step 1: Create a CloudFront Distribution
aws cloudfront create-distribution \
--origin-domain-name your-awesome-website.com.s3-website-us-east-1.amazonaws.com \
--default-root-object index.html
This 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!
Step 2: Configure Cache Behavior
CloudFront’s default cache settings are good, but I typically customize them for front-end apps:
- Short cache times for HTML files (1 hour or less)
- Longer cache times for static assets with hashed filenames (1 year). Don’t worry; you can always invalidate the cache (the next section covers how to do it)
- Special handling for SPA routing (returning index.html for 404s)
Step 3: Set Up Custom Domain and HTTPS
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.
- Request an SSL certificate through AWS Certificate Manager. Alternatively, create free SSL certificates from Let’s Encrypt and import them instead.
- Add your custom domain to the CloudFront distribution
- Update your DNS(e.g. Route53) to point to the CloudFront distribution
⚠️ Pro Tip: The domain typically takes 15-30 minutes to propagate, but I’ve seen it take up to 24 hours in some regions!
🤖 Automating Deployment with GitHub Actions
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 }}
Code language: PHP (php)
A 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!
🤔 SPA Routing Considerations
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:
- HTTP Error Code: 404
- Response Page Path: /index.html
- HTTP Response Code: 200
🛠️ Troubleshooting Common Issues
I’ve experienced these issues often enough to list them here:
- CORS errors – Don’t forget to set up CORS on your S3 bucket if your front end needs to access it directly.
- Caching headaches – Sometimes CloudFront caches more aggressively than you want. The solution is a careful configuration of cache-control headers and strategic invalidations.
- Routing problems – Check your CloudFront error handling configuration if deep links don’t work.
- “Access Denied” errors – Almost always related to bucket policies. Double-check your permissions!
- Deployment failures – Usually due to AWS credentials or permission issues in your CI/CD pipeline.
🚨 Limitations and Considerations
This setup is fantastic but comes with some limitations:
- No server-side rendering – This is a pure static hosting solution. If you need SSR, consider AWS Amplify or other alternatives.
- Cost at scale – Although cheap for most sites, CloudFront data transfer can add up for very high-traffic applications.
- Limited dynamic functionality – For truly dynamic content, you’ll need to pair this with backend APIs (Lambda, AppSync, etc.).
Next Steps and Advanced Techniques
Once you’ve mastered the basics, consider these advanced techniques:
- Multi-environment deployments – Create separate S3 buckets and CloudFront distributions for dev, staging, and production.
- Preview deployments for PRs – Use GitHub Actions to create temporary deployments for each pull request.
- Blue/green deployments – Implement zero-downtime deployments by swapping S3 origin paths.
- CloudFront Functions – Use CloudFront Functions for URL rewrites, simple authentication, or header manipulation without Lambda@Edge.
Final Thoughts
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 🛠️!
Discover more from CodeSamplez.com
Subscribe to get the latest posts sent to your email.
Leave a Reply