# Static Site Deployment on AWS S3 & CloudFront

A complete guide and template for deploying blazing-fast static websites on AWS infrastructure using S3 for storage and CloudFront as a global CDN.
## Features
- High Performance: Global content delivery via CloudFront's CDN
- Cost Effective: Pay only for what you use (often <$1/month for small sites)
- Secure: Free SSL certificates via AWS Certificate Manager
- Scalable: Automatically handles traffic spikes
- Global Availability: 200+ edge locations worldwide
## Prerequisites
- AWS account (Free tier eligible)
- Static website files (HTML, CSS, JS, images)
- Domain name (optional but recommended)
- AWS CLI installed (optional but helpful)
## Step-by-Step Deployment Guide
### 1. Prepare Your Static Site
Ensure your site works locally and all links are relative (not absolute).
```bash
# Example structure
your-site/
├── index.html
├── css/
│ └── style.css
├── js/
│ └── script.js
└── images/
└── logo.png
- Navigate to AWS S3 Console
- Click "Create bucket"
- Enter a globally unique name (e.g.,
www.yourdomain.com
) - Select region closest to your users
- Uncheck "Block all public access" and acknowledge
- Click "Create bucket"
- Select your bucket in S3 console
- Go to "Properties" tab
- Scroll to "Static website hosting"
- Click "Edit" and select "Enable"
- Specify index document (typically
index.html
) - Click "Save changes"
- Go to "Permissions" tab
- Click "Bucket Policy"
- Add the following policy (replace bucket name):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::www.yourdomain.com/*"
}
]
}
You can upload manually via the AWS Console or use AWS CLI:
# Install AWS CLI if needed
aws configure # Set up your credentials
# Sync local files to S3
aws s3 sync ./your-site s3://www.yourdomain.com --delete
- Navigate to CloudFront Console
- Click "Create distribution"
- Under Origin, select your S3 bucket
- Important: Select "Yes use OAI" and create new OAI
- Under "Default cache behavior", choose "Redirect HTTP to HTTPS"
- Under "Settings", specify your domain name (if you have one)
- Click "Create distribution" (Deployment takes ~15 minutes)
- Request a certificate in AWS Certificate Manager
- Add your domain (e.g.,
yourdomain.com
andwww.yourdomain.com
) - Verify ownership via DNS or email
- Update your CloudFront distribution to use this certificate
For Route 53:
- Create a new hosted zone for your domain
- Create an A record (Alias) pointing to your CloudFront distribution
- For www subdomain, create CNAME or another A record
For other providers:
- Create CNAME record pointing to your CloudFront domain (e.g.,
d123.cloudfront.net
)
Set up automatic deployments when you push to GitHub:
-
Create a new IAM user with S3 and CloudFront permissions
-
Add secrets to your GitHub repo:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_S3_BUCKET
AWS_CLOUDFRONT_DISTRIBUTION_ID
-
Add this GitHub Action workflow (
.github/workflows/deploy.yml
):
name: Deploy to AWS
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy to S3
run: |
aws s3 sync ./ s3://${{ secrets.AWS_S3_BUCKET }} --delete
- name: Invalidate CloudFront Cache
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }} \
--paths "/*"
- S3 Storage: $0.023 per GB/month (first 50TB)
- CloudFront:
- $0.085 per GB data transfer out (first 10TB)
- $0.010 per 10,000 HTTPS requests
- Free Tier:
- 1 TB of CloudFront data transfer out
- 2 million CloudFront requests
- 5 GB of S3 storage
Estimated costs for small sites: <$1/month
Issue: 403 Forbidden when accessing site
- ✅ Verify bucket policy allows public read access
- ✅ Check if objects are publicly accessible in S3
Issue: Changes not appearing
- ✅ CloudFront cache takes time to invalidate (default 24h)
- ✅ Manually invalidate cache in CloudFront console
Issue: Mixed content warnings
- ✅ Ensure all resources use HTTPS URLs
- ✅ Set up proper redirects from HTTP to HTTPS
- In S3 static hosting settings, specify error document (e.g.,
404.html
) - In CloudFront, create custom error responses
Configure CloudFront to return index.html
for all paths:
- Go to your distribution
- Under "Error pages", create custom error response
- HTTP error code: 403 (or 404)
- Customize error response: Yes
- Response page path:
/index.html
- HTTP response code: 200
MIT
## Key Features of This README:
1. Comprehensive Step-by-Step Guide:
- From S3 bucket creation to CloudFront configuration
- Includes both console and CLI methods
2. Visual Architecture Diagram:
- Shows the relationship between components
3. CI/CD Automation:
- Ready-to-use GitHub Actions workflow
4. Cost Optimization:
- Clear pricing breakdown and free tier information
5. Troubleshooting Section:
- Solutions for common issues
6. Advanced Configurations:
- SPA routing, custom error pages, etc.
7. Multiple Deployment Methods:
- Manual uploads and automated CI/CD
Need a quick setup walkthrough? Follow my step-by-step guide here medium-walkthrough Don't forget to clap if you see it helpful