CloudFront is AWS's global CDN โ 600+ Points of Presence that cache your content at the edge, terminate TLS, and run code within milliseconds of your users. Route 53 is AWS's authoritative DNS service with intelligent traffic routing: latency-based, geolocation, weighted, and failover policies. Together they form the outermost layer of every serious AWS architecture โ Route 53 directs users to the right endpoint, CloudFront delivers content fast and securely from the edge. Understanding how CloudFront cache policies, behaviors, and Lambda@Edge interact โ and how Route 53 routing policies, health checks, and Alias records work โ is mandatory for AWS Solutions Architect exams and production system design interviews alike.
Client (Sydney)
โ
โผ
Nearest PoP (Sydney ap-southeast-2 edge)
โ
โโโ Cache HIT โโโโโโโโโโโโโโโโโโโโโโโโโโโโ Return cached response (< 5ms)
โ
โโโ Cache MISS
โ
โผ
Regional Edge Cache (Tokyo, ap-northeast-1)
โ
โโโ Cache HIT โโโโโโโโโโโโโโโโโโโโ Return cached response (< 20ms)
โ
โโโ Cache MISS
โ
โผ
Origin Shield (optional, single region)
โ
โผ
Origin (S3 / ALB / API Gateway / EC2)
โ
โโโโ Response cached at Regional Edge Cache โ PoP โ Client
Key insight: CloudFront has two cache tiers. The PoP is close to users (600+ globally). The Regional Edge Cache (~13 worldwide) sits between PoPs and origins, dramatically reducing origin load for cache misses. Origin Shield adds a third optional cache layer that collapses all PoP-to-origin traffic into a single region, cutting origin requests by 85%+ for high-traffic distributions.
Distribution: the top-level CloudFront resource. Has a domain name like d1234abcd.cloudfront.net. You attach a custom domain via Route 53 + ACM certificate.
Origins: where CloudFront fetches content on a cache miss.
Behaviors: ordered rules that match URL path patterns to origins and cache policies.
| Path Pattern | Origin | Cache Policy | Use Case |
|---|---|---|---|
/api/* | API Gateway | CachingDisabled | Dynamic API โ forward auth header |
/static/* | S3 | CachingOptimized | JS/CSS/images โ long TTL |
/images/* | S3 | CachingOptimized (1yr) | Images with versioned filenames |
* (default) | S3 SPA bucket | Managed-CachingOptimized (short) | SPA index.html |
Behaviors are evaluated in order; more specific patterns first. The default * is always last.
Points of Presence: 600+ edge locations in 90+ cities across 47 countries. Each PoP has compute (for CloudFront Functions / Lambda@Edge) and a cache layer.
Regional Edge Caches: 13 locations (North Virginia, Ohio, Oregon, Sรฃo Paulo, Dublin, Frankfurt, Singapore, Tokyo, Sydney, Mumbai, Seoul, Stockholm, Cape Town). Larger cache capacity than PoPs; serve as an intermediate tier.
OAC is the modern way to secure S3 origins โ only CloudFront can read from the bucket. Replace any legacy Origin Access Identity (OAI) with OAC for new distributions; OAI is deprecated.
// S3 bucket policy โ allows only your specific CloudFront distribution
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipal",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/EDFDVBD6EXAMPLE"
}
}
}
]
}
OAC vs OAI:
| Feature | OAC (new) | OAI (legacy) |
|---|---|---|
| Supports all S3 regions | Yes | No (not China/GovCloud) |
| Supports S3 SSE-KMS | Yes | No |
| Key rotation | Automatic (CloudFront managed) | Manual |
| Supports POST/PUT to S3 | Yes | No |
| Bucket policy principal | cloudfront.amazonaws.com | IAM identity |
Bucket must be private (block all public access enabled). Remove any public ACLs.
Cache policies define what goes into the cache key and the TTL rules.
Built-in managed policies:
CachingOptimized โ high cache hit rate; cache key = URL path only; no headers/cookies; TTL default 86400s (1 day), max 31536000s (1 year)CachingDisabled โ TTL = 0; no caching; use for dynamic API responsesCachingOptimized for Uncompressed Objects โ like above but no Accept-Encoding in cache keyCustom cache policy โ when you need fine-grained control:
Cache Policy: my-api-with-auth
TTL:
Min: 0s
Default: 60s
Max: 86400s
Cache Key:
Query strings: Include "version", "lang"
Headers: None (don't include Authorization โ that's an origin request policy concern)
Cookies: Include "session_id"
Compression: gzip + br
TTL resolution (in order of precedence):
Cache-Control: max-age=X from origin โ honored if within [min, max] boundsCache-Control headerOrigin request policies control what CloudFront forwards to the origin on a cache miss. These do NOT affect the cache key.
| Managed Policy | Forwards | Use Case |
|---|---|---|
AllViewer | All headers, query strings, cookies | Dynamic APIs needing full context |
CORS-CustomOrigin | CORS-relevant headers | Cross-origin S3 or custom origin |
CORS-S3Origin | Minimal CORS headers | S3 with CORS |
AllViewerExceptHostHeader | All except Host | Most API Gateway integrations |
UserAgentRefererHeaders | User-Agent, Referer | Analytics-aware origins |
Rule: Keep cache key minimal (only what differentiates responses). Put everything else in origin request policy if origin needs it.
Add security headers to every response without touching origin code.
// SecurityHeadersPolicy โ attach to any behavior
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'
This is the correct place for security headers โ not Lambda@Edge, not the origin. Zero performance cost.
# Invalidate all objects (costs $0.005/path after 1000 free paths/month)
aws cloudfront create-invalidation \
--distribution-id EDFDVBD6EXAMPLE \
--paths "/*"
# Targeted invalidation โ only specific paths
aws cloudfront create-invalidation \
--distribution-id EDFDVBD6EXAMPLE \
--paths "/index.html" "/manifest.json" "/service-worker.js"
Better approach for deployments: versioned filenames
# Instead of invalidating app.js:
app.abc123.js # content-addressed hash in filename
app.v2.js # version in filename
# index.html stays the same name โ short TTL (60s) or targeted invalidation
# All assets use long TTL (1 year) + versioned names โ free, instant rollout
When invalidation is the right tool:
Cost: first 1000 invalidation paths/month free; $0.005/path after. /* counts as 1 path but invalidates everything.
Origin Group: my-failover-group
โโโ Primary: my-s3-bucket-us-east-1
โโโ Failover: my-s3-bucket-us-west-2
Failover on: 500, 502, 503, 504 (configurable)
CloudFront attempts the primary origin. On a qualifying HTTP error status, it immediately retries the request against the failover origin. This is active-passive โ traffic only goes to failover when primary fails.
Optional additional caching layer between Regional Edge Caches and your origin.
Without Origin Shield:
13 Regional Edge Caches โ each sends separate cache-miss requests to origin
With Origin Shield (us-east-1):
13 Regional Edge Caches โ all route through Origin Shield โ 1 request to origin per unique object
| Dimension | CloudFront Functions | Lambda@Edge |
|---|---|---|
| Execution location | PoP (600+ locations) | Regional Edge Cache (~13 locations) |
| Max memory | 2 MB | 128 MB โ 10 GB |
| Max package size | 10 KB | 1 MB (ZIP) / 50 MB (container) |
| Max execution time | 1 ms | 5 s (viewer) / 30 s (origin) |
| Language | JavaScript (ES5.1 subset) | Node.js, Python |
| Network access | No | Yes |
| Environment variables | No | Yes |
| Cost | $0.10/M invocations | $0.60/M executions + $0.00000625/128MB-second |
| Triggers | Viewer Request, Viewer Response | Viewer Request, Viewer Response, Origin Request, Origin Response |
| Cold starts | None | Yes (but warmed across region) |
| Access to request body | No | Yes (Origin Request/Response) |
Decision rule: CloudFront Functions for anything that runs in < 1ms with no external calls. Lambda@Edge for everything else.
CloudFront Functions โ URL normalization example:
// CloudFront Function: normalize URL path and add security header
// Runs at viewer request โ before cache lookup
function handler(event) {
var request = event.request;
var uri = request.uri;
// Remove trailing slash (except root)
if (uri !== '/' && uri.endsWith('/')) {
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: {
location: { value: uri.slice(0, -1) }
}
};
}
// SPA routing: no extension โ serve index.html
if (!uri.includes('.')) {
request.uri = '/index.html';
}
return request;
}
Lambda@Edge โ JWT validation at viewer request:
// Lambda@Edge: validate JWT before allowing access to origin
// Node.js 18.x, deployed to us-east-1, replicated globally
const jwt = require('jsonwebtoken');
exports.handler = async (event) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
const authHeader = headers['authorization']?.[0]?.value;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return {
status: '401',
statusDescription: 'Unauthorized',
headers: {
'www-authenticate': [{ key: 'WWW-Authenticate', value: 'Bearer' }],
'content-type': [{ key: 'Content-Type', value: 'application/json' }]
},
body: JSON.stringify({ error: 'Missing or invalid token' })
};
}
try {
const token = authHeader.slice(7);
// PUBLIC_KEY fetched from SSM at cold start, cached in closure
jwt.verify(token, PUBLIC_KEY, { algorithms: ['RS256'] });
return request; // allow through to origin
} catch (err) {
return {
status: '403',
statusDescription: 'Forbidden',
body: JSON.stringify({ error: 'Token invalid or expired' })
};
}
};
Lambda@Edge gotchas:
us-east-1 โ CloudFront replicates to edge locations automaticallyus-east-1Use these to restrict CloudFront delivery to authenticated users.
Signed URL: access to a single specific object.
https://d1234.cloudfront.net/private/video.mp4
?Expires=1735689600
&Signature=<RSA-SHA1-signature>
&Key-Pair-Id=K2JCJMDEHXQW5F
Signed Cookie: access to multiple objects matching a wildcard pattern (/private/*). Better for HLS video with multiple segment files.
Set-Cookie: CloudFront-Expires=1735689600; Domain=d1234.cloudfront.net; Secure; HttpOnly
Set-Cookie: CloudFront-Signature=<sig>; Domain=...; Secure; HttpOnly
Set-Cookie: CloudFront-Key-Pair-Id=K2JCJMDEHXQW5F; Domain=...; Secure; HttpOnly
Key groups (modern approach โ use this, not root account key pairs):
openssl genrsa)# Generate key pair
openssl genrsa -out private_key.pem 2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
# Upload public key to CloudFront
aws cloudfront create-public-key \
--public-key-config \
Name=my-signing-key-2024,\
EncodedKey="$(cat public_key.pem)",\
CallerReference=my-ref-2024
CloudFront PoP
โ (< 1 second delay)
โผ
Kinesis Data Streams
โ
โโโ Kinesis Data Firehose โ S3 โ Athena (ad-hoc queries)
โโโ Lambda consumer โ OpenSearch (real-time dashboards)
Fields available: timestamp, c-ip, cs-uri-stem, sc-status, cs-bytes, time-taken, x-edge-location, x-edge-result-type (Hit/Miss/RefreshHit/Error), cs-uri-query, cs-headers, ...
Standard logs (simpler, cheaper): delivered to S3 every ~5 minutes; no real-time; free (just S3 storage cost). Use for historical audit and compliance rather than operations.
| Component | Price |
|---|---|
| Data transfer out (0โ10 TB/mo) | $0.0085/GB |
| Data transfer out (10โ50 TB/mo) | $0.0080/GB |
| HTTP requests | $0.0075/10K |
| HTTPS requests | $0.0100/10K |
| Origin Shield requests | $0.0075/10K |
| CloudFront Functions | $0.10/M invocations |
| Lambda@Edge invocations | $0.60/M |
| Lambda@Edge duration | $0.00000625 per 128MB-second |
| Cache invalidation (after 1000 paths/mo) | $0.005/path |
CloudFront vs EC2/ALB egress: EC2 data transfer out costs $0.09/GB. CloudFront costs $0.0085/GB โ over 10x cheaper for the same bytes. For high-traffic applications serving static content, CloudFront pays for itself purely on egress savings.
Hosted Zones:
example.com)internal.example.com); same name can shadow public zone inside VPCRecord types:
| Type | Description | Notes |
|---|---|---|
| A | IPv4 address | 93.184.216.34 |
| AAAA | IPv6 address | 2606:2800:220:1:248:1893:25c8:1946 |
| CNAME | Canonical name alias | Cannot be used at zone apex; charged per query |
| Alias | Route 53 extension โ maps to AWS resource | Free queries; can be at apex; health-check aware |
| MX | Mail exchanger | Priority + mail server hostname |
| TXT | Text record | SPF, DKIM, domain verification |
| NS | Name server | Delegated at zone creation; do not change |
| SOA | Start of authority | Auto-managed by Route 53 |
| SRV | Service locator | Host/port/priority/weight for service discovery |
| CAA | Certification Authority Authorization | Controls which CAs can issue certificates |
Alias vs CNAME โ the most tested Route 53 distinction:
| Alias | CNAME | |
|---|---|---|
| At zone apex (example.com) | Yes | No |
| Points to | AWS resource (CloudFront, ALB, API GW, S3, another hosted zone) | Any hostname |
| Query cost | Free | Charged |
| TTL | Set by AWS target (not configurable) | Configurable |
| Health check integration | Yes | No |
| Visible in dig/nslookup | Returns A record | Returns CNAME record |
Rule: Always use Alias for example.com โ CloudFront or ALB. Use CNAME only for subdomains pointing to non-AWS resources.
Returns all values. If multiple values exist, DNS resolver picks randomly. No health checks per record.
www.example.com A Simple โ [1.2.3.4, 5.6.7.8] (both returned, client picks)
Use when you have one endpoint and no failover requirement.
Distributes traffic by percentage weight. All records must share the same name and type.
api.example.com A Weighted Weight=90 โ prod-alb-arn
api.example.com A Weighted Weight=10 โ canary-alb-arn
Routes to the AWS region with lowest measured network latency for the user. Latency is measured from the user's resolver to AWS regions (not your servers specifically).
api.example.com A Latency Region=us-east-1 โ alb-us-east-1
api.example.com A Latency Region=eu-west-1 โ alb-eu-west-1
api.example.com A Latency Region=ap-southeast-1 โ alb-ap-southeast-1
AWS periodically measures latency and updates the routing data. Not real-time per query.
Active-passive DR. Primary record requires a health check. If primary is unhealthy, Route 53 returns secondary.
app.example.com A Failover PRIMARY Health-check-id=hc-1 โ primary-alb
app.example.com A Failover SECONDARY (no health check) โ dr-alb-or-s3-static
Failover is triggered when Route 53 marks the health check unhealthy. DNS TTL must expire before clients see new IP.
Routes based on the geographic location of the DNS resolver (user's IP geolocation database).
shop.example.com A Geolocation Location=Germany โ eu-alb
shop.example.com A Geolocation Location=United States โ us-alb
shop.example.com A Geolocation Location=Default โ global-alb
Routes based on distance between user and AWS resources, with optional bias adjustment. Requires Route 53 Traffic Flow (visual editor, $50/month/policy record).
Geoproximity Policy:
us-east-1 region bias=+25 (expand effective area)
eu-west-1 region bias=0
Bias range: -99 (shrink coverage area) to +99 (expand). Use when latency routing isn't precise enough for geographic distribution requirements.
Returns up to 8 healthy records randomly. Route 53 omits unhealthy records from the response.
service.example.com A Multivalue Health-check=hc-1 โ 10.0.1.10
service.example.com A Multivalue Health-check=hc-2 โ 10.0.1.11
service.example.com A Multivalue Health-check=hc-3 โ 10.0.1.12
Not a load balancer โ client picks from returned IPs; distribution is approximate. Better than Simple for resilience because unhealthy IPs are excluded from DNS responses.
Types:
Configuration:
Endpoint: https://api.example.com/health
Protocol: HTTPS
Port: 443
Path: /health
Interval: 30s (standard) or 10s (fast โ 2x cost)
Threshold: 3 consecutive failures = unhealthy
String matching: response body must contain "OK"
Pricing:
Health check โ routing: attach a health check to Failover, Weighted, Latency, Multivalue, or Geolocation records. Route 53 automatically excludes unhealthy records from DNS responses.
Calculated health check pattern for regional failover:
Parent: "us-east-1 healthy"
โโโ Child: ALB health check (HTTP 200 on /health)
โโโ Child: RDS writable check (CloudWatch alarm: DatabaseConnections > 0)
โโโ Child: DynamoDB replication lag (CloudWatch alarm)
โ Region is "healthy" only if all three children are healthy
โ Calculated check drives Failover routing policy
Cryptographically signs DNS records to prevent cache poisoning (DNS spoofing). Route 53 supports DNSSEC for public hosted zones.
# Enable DNSSEC signing for a hosted zone
aws route53 enable-hosted-zone-dnssec \
--hosted-zone-id Z1234567890ABC
# Route 53 creates a KSK (Key Signing Key) in AWS KMS
# KSK signs the ZSK (Zone Signing Key) which signs DNS records
# You must add DS record at domain registrar to complete chain of trust
us-east-1 for Route 53DNSSECInternalFailure CloudWatch metricThe default DNS resolver in every VPC lives at VPC CIDR + 2 (e.g., 10.0.0.2 for 10.0.0.0/16). It resolves both public DNS and private hosted zone records.
Hybrid DNS โ connecting on-premises with AWS DNS:
On-Premises Network โโโโโโโโโโโโโโโโโโโโโ AWS VPC
Corporate DNS server Route 53 Resolver
(resolves corp.example.com) (resolves aws.internal.example.com)
Problem: On-prem can't resolve aws.internal.example.com
AWS Lambda can't resolve corp.example.com
Inbound Resolver Endpoint: allows on-premises DNS to forward queries to Route 53.
On-premises DNS โ Inbound Endpoint (ENIs in your VPC, e.g., 10.0.1.5, 10.0.1.6)
โ Route 53 Resolver
โ Private hosted zone record
Outbound Resolver Endpoint: allows VPC resources to forward queries to on-premises DNS.
VPC resource queries corp.example.com
โ Route 53 Resolver checks Resolver Rules
โ Forwarding rule: corp.example.com โ 192.168.1.53 (on-prem DNS)
โ Outbound Endpoint (ENIs) โ on-premises DNS server
Resolver Rules:
ARC provides tooling for reliable multi-region failover beyond basic DNS health checks.
Readiness Checks: continuously verify that your DR resources are actually ready.
Readiness Check: "us-west-2 DR is ready"
โโโ EC2 Auto Scaling Group capacity โฅ 10 instances
โโโ RDS replica lag < 5 minutes
โโโ DynamoDB Global Table replication status = IN_SYNC
โ If any check fails, ARC alerts BEFORE you need to fail over
Routing Controls: on/off switches for traffic, independent of health checks.
Routing Control: us-east-1-traffic [ON]
Routing Control: us-west-2-traffic [OFF]
โ Toggle routing controls to shift traffic manually or via automation
โ Control plane is distributed across 5 cells โ highly available even during regional failure
Zonal Shift (ALB/NLB native feature): immediately shift traffic away from an impaired Availability Zone.
# Shift traffic away from AZ us-east-1b for 3 hours
aws arc-zonal-shift start-zonal-shift \
--resource-identifier arn:aws:elasticloadbalancing:us-east-1:123:loadbalancer/app/my-alb/abc \
--away-from us-east-1b \
--expires-in 3h \
--comment "AZ impairment detected, shifting traffic"
No DNS TTL delay โ ALB/NLB stops routing new connections to the impaired AZ within seconds.
Route 53 doubles as a domain registrar:
.com for $14/year, .io for $42.50/year# Check domain availability
aws route53domains check-domain-availability \
--region us-east-1 \
--domain-name myapp.com
# Register domain
aws route53domains register-domain \
--region us-east-1 \
--domain-name myapp.com \
--duration-in-years 1 \
--admin-contact file://contact.json \
--auto-renew
Browser
โ
โผ
Route 53 (Alias record โ CloudFront distribution)
โ
โผ
CloudFront (custom domain: app.example.com, ACM cert)
โ
โโโ Behavior: /api/*
โ Origin: API Gateway (Regional)
โ Cache Policy: CachingDisabled
โ Origin Request Policy: AllViewerExceptHostHeader
โ (Authorization header forwarded to API GW)
โ
โโโ Behavior: /static/*
โ Origin: S3 (OAC)
โ Cache Policy: CachingOptimized (max-age=31536000)
โ Compress: true
โ
โโโ Default Behavior: /*
Origin: S3 (OAC)
Cache Policy: Custom (max-age=60 โ short for index.html)
Custom Error Response: 403/404 โ /index.html, 200 (SPA routing)
Response Headers Policy: SecurityHeadersPolicy
WAF ACL: attached (rate limiting, geo-blocking)
Custom error response is critical for SPA routing โ S3 returns 403/404 for routes like /dashboard, and CloudFront must remap those to index.html with a 200 status so the React/Vue router handles navigation.
ACM certificate: must be in us-east-1 for CloudFront (even if your stack is in eu-west-1). Request via ACM, validate via Route 53 DNS validation (automatic CNAME record).
Users worldwide
โ
โผ
Route 53 โ Latency routing policy
โ
โโโ us-east-1 ALB [Health check: hc-east]
โ โ
โ โโโ ECS Fargate service (app)
โ โโโ RDS Aurora Global (primary writer)
โ
โโโ eu-west-1 ALB [Health check: hc-eu]
โ โ
โ โโโ ECS Fargate service (app)
โ โโโ RDS Aurora Global (reader endpoint)
โ
โโโ ap-southeast-1 ALB [Health check: hc-ap]
โ
โโโ ECS Fargate service (app)
โโโ RDS Aurora Global (reader endpoint)
DynamoDB Global Tables: replicated across all 3 regions
S3 Cross-Region Replication: assets in all regions
Route 53 ARC:
Routing controls: manual override per region
Readiness checks: Aurora replication lag, ECS desired vs running
Write routing: for strong consistency, writes must go to the primary Aurora region. Either:
us-east-1 specifically (separate DNS record)Failover sequence: health check fails in eu-west-1 โ Route 53 removes eu-west-1 from Latency policy โ users who previously resolved to EU now resolve to nearest healthy region (us-east-1 or ap-southeast-1)
Phase 1 (production = Blue):
app.example.com Weighted Weight=100 โ blue-alb.example.com
app.example.com Weighted Weight=0 โ green-alb.example.com
Phase 2 (canary โ 10% to Green):
app.example.com Weighted Weight=90 โ blue-alb.example.com
app.example.com Weighted Weight=10 โ green-alb.example.com
Phase 3 (50/50 validation):
app.example.com Weighted Weight=50 โ blue-alb
app.example.com Weighted Weight=50 โ green-alb
Phase 4 (full cutover):
app.example.com Weighted Weight=0 โ blue-alb (keep for rollback)
app.example.com Weighted Weight=100 โ green-alb
Rollback: flip weights back to 100/0 in favor of blue
DNS TTL consideration: set TTL to 60s or lower before starting migration so failback is fast. After migration stabilizes, set TTL back to 300s.
Day 1 โ Setup (no traffic impact):
1. Create hosted zone in Route 53 for example.com
2. Recreate all DNS records in Route 53 exactly as at current registrar
3. Note Route 53 name servers (4 NS records in hosted zone)
Day 2 โ Lower TTL at current registrar:
4. Reduce TTL on all records to 60s
5. Wait 24โ48 hours (old TTL must fully expire everywhere)
Day 3 โ Migrate:
6. At current registrar: update NS records to Route 53 NS servers
7. DNS propagation: 24โ48 hours (TTL was 60s so most resolvers catch up quickly)
8. Monitor: dig example.com NS โ watch for Route 53 NS to appear
Day 7+ โ Cleanup:
9. Transfer domain registration to Route 53 (optional โ can keep registrar elsewhere)
10. Restore TTLs to 300s
Never change nameservers and records simultaneously โ change records first, verify, then change NS.
Q: Explain how CloudFront caching works. How would you configure it for a React SPA with an API backend?
A: CloudFront caches at two tiers: 600+ PoPs close to users and 13 Regional Edge Caches behind them. For a React SPA: two behaviors โ /api/* points to API Gateway with CachingDisabled policy and AllViewerExceptHostHeader origin request policy so Authorization headers reach the API; the default /* behavior points to S3 with a short TTL (60s) for index.html and a custom error response mapping 403/404 to /index.html with 200 status for client-side routing. Static assets use versioned filenames and a 1-year max-age cache policy. Attach a SecurityHeadersPolicy response policy to the default behavior for free security headers across all responses.
Q: What's the difference between Origin Access Control and Origin Access Identity?
A: Both restrict S3 bucket access to CloudFront only, preventing direct S3 URL access. OAI is the legacy mechanism using a special IAM identity โ doesn't work in all S3 regions, doesn't support SSE-KMS encrypted buckets, requires manual key rotation. OAC is the replacement: uses cloudfront.amazonaws.com as the service principal with a condition on the specific distribution ARN. More secure, supports all regions, supports SSE-KMS, supports signed requests (POST/PUT). Always use OAC for new distributions.
Q: When would you use Lambda@Edge vs CloudFront Functions?
A: CloudFront Functions for anything sub-millisecond with no I/O: URL normalization, simple redirects, A/B test cookie assignment, adding security headers (though a response headers policy is better for that), light authentication token format checking. Lambda@Edge when you need: network calls (fetch user attributes from DynamoDB, call auth service), Node.js packages, meaningful compute (image resizing, SSR), request body access (origin triggers only), or timeouts > 1ms. Lambda@Edge costs 6x more per invocation and runs at only 13 locations vs 600+ for CloudFront Functions.
Q: How do you implement signed URLs for private content in CloudFront?
A: Create a key group: generate RSA-2048 key pair locally, upload the public key to CloudFront, create a key group containing it, attach the key group to the distribution behavior for your private content. In your application (typically a Lambda behind API Gateway), sign URLs server-side using the private key โ include the resource URL, expiration timestamp, and IP restriction (optional). The signature is RSA-SHA1 over a policy document. For HLS video or multiple files, use signed cookies instead โ three cookies (Policy, Signature, Key-Pair-Id) scoped to a domain/path wildcard. Never expose the private key โ store in Secrets Manager and load at Lambda warm start.
Q: Explain the difference between Route 53 CNAME and Alias records.
A: CNAME is a standard DNS record that aliases one hostname to another. It resolves to a hostname, which requires a second DNS lookup. It cannot be used at the zone apex (example.com). Queries are charged. Alias is a Route 53 extension that behaves like CNAME but resolves directly to the IP of an AWS resource (CloudFront, ALB, API Gateway, S3 website endpoint, or another Route 53 hosted zone). Alias can be at the zone apex, queries are free, and Alias records inherit the health check awareness of the target resource. For example.com โ CloudFront, you must use Alias. For www.example.com โ some external CDN, CNAME works fine.
Q: How does Route 53 failover routing work? What triggers the failover?
A: You create two records with the same name โ one PRIMARY and one SECONDARY. The primary record requires a health check. Route 53 continuously polls the health check endpoint from 15 global health checker locations. If more than 18% of health checkers report unhealthy over the configured threshold (default: 3 consecutive failures), Route 53 marks the check unhealthy. DNS responses then return only the secondary record. Clients with cached DNS must wait for TTL expiry before seeing the change โ set TTL to 60s for DR scenarios. Note: Route 53 health checks originate from AWS IP ranges; make sure your firewall allows them.
Q: Design a multi-region active-active architecture using Route 53 and DynamoDB.
A: Route 53 Latency routing with health checks on ALBs in 3+ regions (us-east-1, eu-west-1, ap-southeast-1). Each region runs an independent application stack: ECS Fargate or Lambda, behind an ALB, reading/writing DynamoDB Global Tables (multi-region, multi-master). DynamoDB Global Tables uses last-writer-wins conflict resolution โ model your writes to avoid conflicts or use a conditional write pattern. For read-heavy workloads, DAX in each region. CloudFront in front of static assets in all regions. Route 53 ARC for readiness checks (DynamoDB replication lag < threshold) and routing controls for controlled failover. S3 with Cross-Region Replication for static asset consistency. ACM certs in each region for ALB + one in us-east-1 for CloudFront.
Q: How would you migrate a domain from another registrar to Route 53 with zero downtime?
A: Never touch nameservers and DNS records simultaneously. Step 1: create a Route 53 hosted zone and replicate all records exactly. Step 2: lower TTL on all records at the current registrar to 60s; wait for old TTL to expire (24โ48 hours). Step 3: update the NS records at the current registrar to the 4 Route 53 nameservers. Monitor with dig example.com NS +trace. DNS propagates within minutes to hours since TTL is 60s. Step 4: after 48 hours, optionally initiate domain transfer to Route 53 (requires unlocking domain, getting auth code, 60-day post-transfer lock). Restore TTLs after stable.
Q: What is Route 53 Application Recovery Controller and when would you use it?
A: ARC is a set of tools for operationalizing multi-region failover. It has three components: Readiness Checks (continuously verify that DR resources โ ASG capacity, DB replication lag, etc. โ are ready before you need them), Routing Controls (on/off traffic switches that live in a 5-cell distributed control plane, highly available even during regional outages, used for deterministic traffic shifting), and Zonal Shift (instantly move ALB/NLB traffic away from a specific AZ without DNS TTL delays). Use ARC when you need reliability guarantees that go beyond "health check โ DNS failover" โ specifically when you need to test DR readiness, require manual failover with audit trail, or need sub-minute AZ-level traffic shifts.
Q: How do you add security headers to all CloudFront responses without modifying the origin?
A: Create a Response Headers Policy in CloudFront with the desired headers (HSTS, X-Frame-Options, X-Content-Type-Options, CSP, Referrer-Policy). Attach it to the distribution behavior. CloudFront injects these headers into every response before returning to the client โ no Lambda@Edge, no origin changes needed. You can use the managed SecurityHeadersPolicy or create a custom one. For CSP with nonces (required for inline scripts), you do need Lambda@Edge at origin response to generate per-request nonces, but for static CSP this is zero-cost and instant.
Q: A CloudFront distribution is not caching. What are the most common causes?
A:
Cache-Control: no-cache or no-store from origin โ CloudFront respects these; override with cache policy min/max TTL to force caching regardless.* behavior instead of your configured behavior.CachingDisabled policy applied to a behavior that should be cached.Check x-cache: Miss from cloudfront vs Hit from cloudfront headers; examine x-edge-result-type in real-time logs.
us-east-1 even if your stack is elsewhere/* on every deployment โ use versioned asset filenames instead; invalidation is a signal that your deployment strategy is wrongexample.com CNAME myapp.example.com is invalid DNS; use Alias record for apex