Docs
/
AWS Cloud
Chapter 10
10 — CloudFront & CDN
What is CloudFront?
AWS Content Delivery Network — caches content at 400+ edge locations worldwide for fast delivery.
Without CDN:
User (Tokyo) → Origin (us-east-1): 200ms
With CloudFront:
User (Tokyo) → Edge (Tokyo): 10ms ← cache hit
User (Tokyo) → Edge → Origin: 200ms ← cache miss (first request only)
How It Works
┌──────────┐ ┌──────────────┐ ┌─────────────┐
│ Client │ ──→ │ CloudFront │ ──→ │ Origin │
│ (browser) │ │ Edge Location│ │ S3 / ALB / │
└──────────┘ └──────────────┘ │ API Gateway │
Cache hit? ✅ └─────────────┘
Return cached Only on cache miss
Distribution Setup
# Origins: where CloudFront fetches content from
# - S3 bucket (static files)
# - ALB / EC2 (dynamic API)
# - API Gateway
# - Custom HTTP server
# Behaviors: rules for how to cache
# /api/* → ALB origin, no caching
# /static/* → S3 origin, cache 1 year
# /* → S3 origin, cache 1 day
Cache Control
Cache-Control headers from origin:
Cache-Control: public, max-age=31536000 → Cache 1 year (static assets)
Cache-Control: no-cache → Revalidate every request
Cache-Control: no-store → Never cache
Cache-Control: private, max-age=0 → Don't cache (user-specific)
Cache Key
CloudFront caches based on:
- URL path
- Query strings (configurable: none, whitelist, all)
- Headers (configurable)
- Cookies (configurable)
/api/users?page=1 → different cache entry than
/api/users?page=2
Tip: Only include necessary query strings in cache key
to maximize cache hit ratio
Invalidation
Force CloudFront to re-fetch from origin.
# Invalidate specific paths
aws cloudfront create-invalidation \
--distribution-id E1234567890 \
--paths "/index.html" "/static/app.js"
# Invalidate everything
aws cloudfront create-invalidation \
--distribution-id E1234567890 \
--paths "/*"
# Better approach: use versioned filenames
# /static/app.abc123.js → never needs invalidation
# Change filename on each deploy (handled by webpack/vite hash)
Lambda@Edge / CloudFront Functions
Run code at edge locations.
| CloudFront Functions | Lambda@Edge | |
|---|---|---|
| Runtime | JavaScript | Node.js, Python |
| Execution time | < 1ms | Up to 30s |
| Memory | 2 MB | 128-10240 MB |
| Network access | No | Yes |
| Cost | 1/6th of Lambda@Edge | Higher |
| Use cases | URL rewrites, header manipulation, redirects | Auth, A/B testing, dynamic content |
// CloudFront Function: URL rewrite for SPA
function handler(event) {
var request = event.request;
var uri = request.uri;
// SPA: serve index.html for all non-file routes
if (!uri.includes('.')) {
request.uri = '/index.html';
}
return request;
}
Signed URLs / Signed Cookies
Restrict access to private content.
import { getSignedUrl } from '@aws-sdk/cloudfront-signer';
const signedUrl = getSignedUrl({
url: 'https://d123.cloudfront.net/premium/video.mp4',
keyPairId: 'K1234567890',
privateKey: privateKeyString,
dateLessThan: new Date(Date.now() + 3600 * 1000).toISOString(), // 1 hour
});
S3 + CloudFront (Static Site)
S3 bucket (private, no public access)
↓
CloudFront (Origin Access Control)
↓
Custom domain + ACM certificate (HTTPS)
↓
Route 53 DNS
Benefits:
- HTTPS everywhere
- Global edge caching
- S3 stays private (no direct access)
- DDoS protection (AWS Shield)
Key Takeaways
- CloudFront = global CDN with 400+ edge locations
- Use versioned filenames for static assets to avoid invalidation
- Configure Cache-Control headers at the origin for proper caching behavior
- CloudFront Functions for simple tasks (URL rewrite, redirects) — cheapest option
- Lambda@Edge for complex logic (auth, dynamic content)
- Use Origin Access Control to keep S3 private while serving via CloudFront
- Signed URLs for private/premium content distribution