I recently optimized my appβs image delivery by moving from direct storage / Vercel handling to a scalable CDN setup using AWS. Hereβs the complete step-by-step breakdown.
π§ Problem
- Images were slow to load globally
- Vercel image optimization was becoming costly
- Direct S3 access wasnβt efficient or secure
π― Goal
Build a system like:
User β CloudFront (CDN) β S3 (Private Storage)
ποΈ Architecture
- Amazon S3 β stores images
- Amazon CloudFront β CDN layer
- Route 53 β DNS
- ACM (SSL) β HTTPS
βοΈ Step-by-Step Setup
1. Upload images to S3
Organize structure like:
- /banner
- /products
- /users
2. Create CloudFront Distribution
- Origin: S3 bucket
- Origin Access: β Private (recommended)
- Cache: Default optimized settings
- Protocol: Redirect HTTP β HTTPS
3. Configure Custom Domain
- Add:
cdn.yourdomain.com - Attach SSL certificate (ACM)
- Create CNAME in Route 53 β CloudFront
4. Test CDN
Example:
https://cdn.yourdomain.com/banner/image.jpg
β‘ Performance Optimization (IMPORTANT)
Add Cache Headers in S3
Set:
Cache-Control: public, max-age=31536000, immutable
This ensures:
- 1-year caching
- Instant repeat loads
- Reduced AWS costs
Bulk Update (CLI)
aws s3 cp s3://your-bucket/banner/ s3://your-bucket/banner/ \
--recursive \
--metadata-directive REPLACE \
--cache-control "public, max-age=31536000, immutable"
Invalidate CloudFront Cache
After changes:
/*
π₯ Results
- β‘ Faster global image delivery
- πΈ Reduced Vercel costs
- π Secure (private S3 via CloudFront)
- π Scalable for production
β οΈ Lessons Learned
- Donβt use root domain (use subdomain like cdn.domain.com)
- Always invalidate cache after metadata changes
- Never reuse image filenames (use versioning)
π§© Bonus (Next.js Config)
module.exports = {
images: {
domains: ['cdn.yourdomain.com'],
unoptimized: true
}
};
π Conclusion
S3 + CloudFront is one of the most cost-efficient and scalable ways to serve images in production. Once set up, it just works.
If you're building a startup or scaling your app β this setup is a must.
Happy building π
Top comments (0)