CloudFront is AWS's CDN — it caches content at edge locations (300+ globally) close to users. Instead of every user hitting your S3 bucket in us-east-1, they hit the nearest edge location.
Real-World: Your app serves a React SPA from S3 + a REST API from Lambda. Without CloudFront: Australian users hit US servers, 200ms latency minimum. With CloudFront: static assets cached in Sydney edge, API calls still go to US but cached responses served from Sydney.
User (Sydney) → CloudFront Edge (Sydney)
↓ Cache miss → Origin (S3 in us-east-1)
↓ Cache hit → Return from edge (< 10ms)
| Origin Type | Use for |
|---|---|
| S3 Bucket | Static websites, file downloads |
| EC2 / ALB | Dynamic content, APIs |
| Lambda Function URL | Serverless APIs |
| Custom HTTP origin | Any HTTP server |
For S3 origins — S3 bucket should NOT be public. Only CloudFront can access it:
// S3 Bucket Policy with OAC
{
"Effect": "Allow",
"Principal": {"Service": "cloudfront.amazonaws.com"},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123:distribution/ABC123"
}
}
}OAC replaces OAI (Origin Access Identity) — use OAC for new distributions.
By default, CloudFront caches per URL path. Customize to include:
- Query strings:
?version=2→ different cache entry - Headers:
Accept-Language→ different response per language - Cookies: session-based content
Minimize cache key for better cache hit ratio. More cache key variables = more cache misses.
Origin response headers:
Cache-Control: max-age=86400 → CloudFront respects this
Cache-Control: no-cache → CloudFront still caches unless you configure otherwise
CloudFront distribution settings:
Minimum TTL: 0
Default TTL: 86400 (24 hours)
Maximum TTL: 31536000 (1 year)
When you update content in S3, CloudFront might still serve old cached version.
# Invalidate specific path
aws cloudfront create-invalidation \
--distribution-id ABCDEFG123456 \
--paths "/index.html" "/static/app.js"
# Invalidate everything (expensive — counts against free tier)
aws cloudfront create-invalidation \
--distribution-id ABCDEFG123456 \
--paths "/*"Better approach: Use versioned file names. app.v2.js instead of app.js. Cache-bust by changing filename.
| Setting | What it means |
|---|---|
| HTTP and HTTPS | Allow both (not recommended) |
| Redirect HTTP to HTTPS | Force HTTPS |
| HTTPS Only | Block HTTP requests |
Always use "Redirect HTTP to HTTPS" for production.
Run code at CloudFront edge locations to customize behavior:
| Feature | Lambda@Edge | CloudFront Functions |
|---|---|---|
| Runtime | Node.js, Python | JavaScript only |
| Trigger points | Viewer request/response, Origin request/response | Viewer request/response only |
| Timeout | 5s (viewer), 30s (origin) | 1ms |
| Memory | Up to 10GB | 2MB |
| Network access | Yes | No |
| Cost | Higher | 1/6th the cost |
| Use for | Dynamic manipulation, auth, A/B testing | URL rewrites, header manipulation |
A/B Testing:
// Lambda@Edge: Viewer Request
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const userAgent = request.headers['user-agent'][0].value;
// 50% go to variant B
if (Math.random() < 0.5) {
request.uri = '/variant-b' + request.uri;
}
callback(null, request);
};JWT Validation at Edge:
// Validate token before hitting origin
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const token = request.headers['authorization']?.[0]?.value;
if (!validateJWT(token)) {
callback(null, {
status: '401',
body: 'Unauthorized'
});
return;
}
callback(null, request);
};URL Normalization (remove trailing slash, lowercase):
function handler(event) {
const request = event.request;
const uri = request.uri;
// Redirect /about/ to /about
if (uri.endsWith('/') && uri !== '/') {
return {
statusCode: 301,
headers: { location: { value: uri.slice(0, -1) } }
};
}
// Default to index.html for SPA
if (!uri.includes('.')) {
request.uri = '/index.html';
}
return request;
}For paid content or private downloads:
Signed URL: Access to a single file.
from botocore.signers import CloudFrontSigner
import rsa, datetime
def generate_signed_url(url, key_id, private_key_pem):
signer = CloudFrontSigner(key_id, rsa_signer)
signed_url = signer.generate_presigned_url(
url,
date_less_than=datetime.datetime.now() + datetime.timedelta(hours=1)
)
return signed_urlSigned Cookie: Access to multiple files (e.g., all content in /premium/).
| Feature | Signed URL | Signed Cookie |
|---|---|---|
| Access | Single object | Multiple objects with pattern |
| Use for | One video download | Netflix-style subscription |
Block or allow access by country:
Allowlist: Only allow US, CA, GB
Blocklist: Block CN, RU
Based on IP → country mapping (MaxMind database).
- Upload custom certificate to ACM (us-east-1 only)
- SNI (Server Name Indication) — free
- Dedicated IP (for old HTTPS clients) — expensive
Internet → WAF (DDoS, SQL injection, XSS rules) → CloudFront → Origin
Block requests before they hit your origin. Define rate limits, IP blacklists, geo blocks, custom rules.
Route 53 is AWS's managed DNS service. It resolves domain names to IP addresses and enables health-checked routing.
| Record | Purpose | Example |
|---|---|---|
| A | Domain → IPv4 | example.com → 1.2.3.4 |
| AAAA | Domain → IPv6 | example.com → 2001:db8::1 |
| CNAME | Domain → another domain | www → example.com |
| Alias | AWS resource → AWS resource | example.com → ALB |
| MX | Mail server | example.com → mail.example.com 10 |
| TXT | Arbitrary text | Domain verification |
| NS | Name servers | Delegated zones |
| Feature | CNAME | Alias |
|---|---|---|
| Works on zone apex (example.com)? | No | Yes |
| Points to | Any domain | AWS resources only |
| TTL | Configurable | Set by AWS |
| Cost | Free | Free |
Always use Alias for AWS resources (ALB, CloudFront, S3 website, etc.)
| Policy | How it works | Use for |
|---|---|---|
| Simple | One or more IPs, random selection | Single resource |
| Weighted | % of traffic to each resource | A/B testing, gradual migration |
| Latency | Route to lowest-latency region | Global apps |
| Failover | Primary/secondary with health check | DR |
| Geolocation | Route based on user's country/continent | Content localization |
| Geoproximity | Route based on distance (with bias) | Traffic shifting |
| Multi-Value | Multiple healthy IPs (not load balancer) | Client-side load balancing |
| IP-based | Route based on client IP range | CIDR-based routing |
Route 53 Record: api.example.com
→ ALB-us-east-1 Weight: 90 (90% traffic)
→ ALB-us-west-2 Weight: 10 (10% traffic — canary)
Primary: ALB in us-east-1 (health check active)
Secondary: ALB in eu-west-1
If primary fails health check → traffic goes to secondary automatically
Route 53 health checks are separate from EC2 health checks:
- HTTP/HTTPS/TCP health check from 15+ global locations
- Calculate Health: combine multiple health checks (AND/OR)
- CloudWatch alarm: if alarm in ALARM state → treat endpoint as unhealthy
Real-World: Your API returns 200 but serves errors. Route 53 health check can evaluate the HTTP response body for a specific string — if not found, endpoint marked unhealthy.
| Practice | Reason |
|---|---|
| Use Alias records for AWS resources | No extra DNS lookup, works at zone apex |
| Use versioned assets with CloudFront | Avoid cache invalidation costs |
| Set appropriate TTLs | Lower TTL = faster failover, but more DNS queries |
| Use OAC for S3 origins | S3 not publicly accessible |
| Enable Access Logs on CloudFront | Troubleshoot and analyze traffic patterns |
| WAF on CloudFront | Block attacks before reaching origin |
- CloudFront signed URLs vs S3 presigned URLs: Different! CloudFront signed URL serves via CloudFront (faster, can be restricted). S3 presigned = direct S3 access.
- ACM certificate for CloudFront: must be in us-east-1 regardless of where CloudFront distribution is used.
- Route 53 is global — not regional.
- TTL too high: DNS cache stale during failover. For failover scenarios, lower TTL.
- Geolocation vs Latency: Geolocation = user's country. Latency = which endpoint responds fastest.
- CloudFront can handle HTTPS end-to-end: Viewer (HTTPS) → CloudFront → Origin (HTTPS).
- CloudFront origin failover: Primary origin fails → automatically routes to secondary origin.
Q: S3 static website with custom domain and HTTPS? → S3 → CloudFront (OAC) → Route 53 Alias → CloudFront distribution. ACM cert in us-east-1.
Q: Route 10% of traffic to new API version for testing? → Route 53 Weighted Routing (or API Gateway canary, or Lambda alias weighted routing).
Q: Users in EU should be served from EU region? → Route 53 Geolocation routing policy (country → specific ALB).
Q: Automatically failover to backup region when primary is down? → Route 53 Failover routing with health checks on primary endpoint.
Q: Serve static content globally with low latency? → CloudFront with S3 origin and edge caching.