TL;DR: Serving users straight from S3/EC2/ALB bills Data Transfer Out at $0.09/GB. CloudFront's rates start slightly lower ($0.085/GB in NA/EU) and fall to $0.02/GB at volume — and every cache hit is a request your origin never serves. Adding the middleman cuts the bill 30–55% on static-heavy traffic while making the site faster. The savings vanish only when nothing is cacheable.
The numbers
- Direct S3/EC2 egress: ~$0.09/GB (first 10 TB tier) · CloudFront: ~$0.085/GB down to ~$0.02/GB at high volume, plus ~$0.01 per 10k HTTPS requests
- Field examples from the source workflow: a video startup at 80 TB/month went $7,200 → $3,200/month (rate delta + cache hits); a B2B SaaS cut $2,500 → $1,600/month and fixed APAC load times; an e-commerce site saved $2,500/month combined when offloading images also let them downsize the EC2 fleet
- 400+ edge locations; users get served from nearby instead of your origin region
Do this
-
Pull Data Transfer Out from Cost Explorer. Climbing into hundreds/month = a candidate.
-
Start with static assets (images, JS/CSS, fonts, downloads, video): S3 origin + Origin Access Control (bucket stays private), point asset URLs at the distribution. Skip HTML/caching-invalidation complexity until comfortable.
-
Tune the three savings levers:
- TTLs — longer cache lifetimes = fewer origin fetches; aim for an 80%+ cache hit ratio (CloudWatch reports it).
- Compression — let CloudFront gzip/brotli text content; smaller bytes = smaller bill.
- Price class — users mostly in NA/EU? Price Class 100 skips the expensive edge regions entirely.
-
Watch the hit ratio after launch. A low ratio means cache keys are fragmenting (cookies, query strings, headers) — fix the cache policy before judging the savings.
Gotchas
- Personalized/dynamic responses don't cache — you pay origin fetch and CloudFront delivery; purely dynamic APIs can cost slightly more behind a CDN.
- Same-region AWS-to-AWS traffic is already free — putting CloudFront in an internal path adds cost where there was none.
- Request fees exist — billions of tiny objects shift the math; check the per-10k request line.
- Invalidation has costs and lag — design cache-busting into asset URLs (hashed filenames) instead of relying on invalidations.
- At very low traffic (~10 GB/month) the savings are pennies; do it for performance, not cost.
Skip this if
- All traffic is unique-per-user dynamic content with no cacheable layer.
- Users and origin sit in one region and volumes are small.
- The egress you're fighting is internal (service-to-service) — that's VPC Endpoints / NAT consolidation territory, not CDN territory. At sustained 10+ TB/month, also ask AWS about CloudFront private pricing.