DEV Community

arafatruetbd
arafatruetbd

Posted on

How I Built a Production-Ready Image CDN Using AWS S3 + CloudFront (and Eliminated Vercel Image Costs)

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"
Enter fullscreen mode Exit fullscreen mode

Invalidate CloudFront Cache

After changes:

/*
Enter fullscreen mode Exit fullscreen mode

🔥 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
  }
};
Enter fullscreen mode Exit fullscreen mode

🎉 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)