Distribution Configuration Basics
CloudFront sits between your users and your origin — whether that's an S3 bucket, an ALB, or a custom HTTP server. The first decision is your origin configuration, and getting this wrong means either serving stale content or missing the cache entirely.
For S3 origins, use Origin Access Control (OAC) instead of the older Origin Access Identity (OAI). OAC supports SSE-KMS encrypted buckets and works with S3 bucket policies properly. AWS deprecated OAI for a reason.
resource "aws_cloudfront_distribution" "main" {
enabled = true
is_ipv6_enabled = true
default_root_object = "index.html"
price_class = "PriceClass_100"
origin {
domain_name = aws_s3_bucket.assets.bucket_regional_domain_name
origin_id = "s3-assets"
origin_access_control_id = aws_cloudfront_origin_access_control.main.id
}
origin {
domain_name = "api.example.com"
origin_id = "api-backend"
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}
}
default_cache_behavior {
target_origin_id = "s3-assets"
viewer_protocol_policy = "redirect-to-https"
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
compress = true
forwarded_values {
query_string = false
cookies { forward = "none" }
}
min_ttl = 0
default_ttl = 86400
max_ttl = 31536000
}
}
Cache Behaviors for Mixed Architectures
Most real applications need multiple origins behind one distribution. Static assets from S3, API calls to an ALB, and maybe server-rendered pages from a different backend. CloudFront handles this through ordered cache behaviors matched by path pattern.
The ordering matters — CloudFront evaluates path patterns from top to bottom and uses the first match. Put specific patterns before general ones. /api/* before /*.js before the default behavior.
For API origins, don't cache anything by default. Forward all headers, cookies, and query strings. Set TTL to 0. You can selectively cache specific API responses using Cache-Control headers from your backend, but the safe default is to pass everything through.
Cache Invalidation Strategy
The best invalidation strategy is not needing invalidations. Use content-addressed filenames — app.a3f9b2.js instead of app.js. When the content changes, the filename changes, and CloudFront fetches the new file automatically because it's a cache miss.
For files that can't be renamed (index.html, robots.txt, API responses), set shorter TTLs via Cache-Control headers from the origin. index.html with max-age=300 means CloudFront serves the cached version for at most 5 minutes.
When you do need to invalidate, batch your invalidation paths. Each invalidation request can contain up to 3,000 paths. The first 1,000 invalidation paths per month are free; after that it's $0.005 per path. Using a wildcard (/images/*) counts as one path.
Performance Optimization: Price Class Selection
CloudFront has three price classes that control which edge locations serve your content. PriceClass_100 uses only the cheapest regions (US, Canada, Europe, Israel). PriceClass_200 adds most of Asia, Africa, and the Middle East. PriceClass_All uses every edge location.
If 90% of your users are in North America and Europe, PriceClass_100 saves money with minimal latency impact. But if you have meaningful traffic from Asia or South America, the jump to PriceClass_200 is usually worth it — the latency difference between a local edge and a trans-oceanic fetch is noticeable.
Security Headers and WAF Integration
CloudFront can add security headers through response header policies without touching your origin. This is the right place to add HSTS, X-Content-Type-Options, X-Frame-Options, and CSP headers for static sites.
resource "aws_cloudfront_response_headers_policy" "security" {
name = "security-headers"
security_headers_config {
strict_transport_security {
access_control_max_age_sec = 63072000
include_subdomains = true
preload = true
override = true
}
content_type_options { override = true }
frame_options {
frame_option = "DENY"
override = true
}
xss_protection {
mode_block = true
protection = true
override = true
}
}
}
For WAF integration, associate an AWS WAF Web ACL with your CloudFront distribution. The WAF ACL must be in us-east-1 regardless of where your origin lives. Common rules to enable: rate limiting, SQL injection protection, and the AWS managed rule groups for known bad inputs.
Origin Shield: The Extra Cache Layer
Origin Shield adds a centralized caching layer between CloudFront's regional edge caches and your origin. Without it, each regional edge cache fetches independently from the origin on a cache miss. With 13 regional edge caches, that's potentially 13 requests to your origin for the same cold object.
Origin Shield collapses those into a single origin request. The additional cost is $0.0090 per 10,000 requests through Origin Shield. For origins that struggle with traffic spikes, it's cheap insurance against origin overload.
origin {
domain_name = aws_s3_bucket.media.bucket_regional_domain_name
origin_id = "s3-media"
origin_access_control_id = aws_cloudfront_origin_access_control.main.id
origin_shield {
enabled = true
origin_shield_region = "us-east-1"
}
}
Lambda@Edge vs CloudFront Functions
CloudFront Functions run in a restricted JavaScript sandbox with 2MB memory and a 1ms time limit. Lambda@Edge runs full Node.js or Python with up to 10 seconds and 128MB-10GB memory.
For URL normalization, cache key manipulation, and header injection, use CloudFront Functions. They're 1/6th the cost. For anything that needs network calls, use Lambda@Edge.
Real User Monitoring with CloudFront
CloudFront standard logs give you detailed request-level data but are delivered to S3 with variable delay. For real-time monitoring, enable real-time logs that stream to Kinesis Data Streams.
Key metrics to watch: cache hit ratio (target above 85%), origin latency, 4xx/5xx error rates, and bytes transferred by country. A sudden drop in cache hit ratio usually means a deployment broke cache keys.
Multi-Origin Failover
CloudFront origin groups let you configure automatic failover between a primary and secondary origin. If the primary returns a 500, 502, 503, or 504 error, CloudFront retries against the secondary origin transparently.
The practical application: primary ALB in us-east-1 and a secondary ALB in us-west-2 as an origin group. If us-east-1 goes down, CloudFront automatically routes to us-west-2. Combined with Route53 health checks, you get a two-layer failover system.
Compression and Protocol Optimization
CloudFront can compress responses automatically for clients that accept gzip or brotli. Enable compression in the cache behavior configuration. Brotli typically achieves 15-20% better compression than gzip for HTML, CSS, and JavaScript. CloudFront selects the best compression based on the Accept-Encoding header from the client.
For APIs returning JSON, compression is particularly effective. A 50 KB JSON response compresses to about 8 KB with gzip and 6 KB with brotli. At scale, this reduces both data transfer costs and time-to-first-byte for clients. The compression computation happens at the edge, adding negligible latency (~1ms).
HTTP/2 and HTTP/3 are enabled by default on CloudFront distributions. HTTP/3 uses QUIC, which provides better performance on lossy networks (mobile connections, congested WiFi) thanks to its built-in connection migration and improved congestion control. You don't need to configure anything; CloudFront negotiates the best protocol with each client automatically.
For WebSocket connections, CloudFront supports them through the origin protocol policy. Set the origin to use WebSocket (wss://) and configure the cache behavior to forward the Upgrade header. CloudFront maintains persistent connections to the origin, so WebSocket frames pass through without re-establishing connections per message.
Geo-Restriction and Signed URLs
CloudFront offers two levels of access control: geo-restriction (allowlist or blocklist by country) and signed URLs/cookies for authenticated content delivery. Geo-restriction uses GeoIP databases and is applied at the edge before the request reaches your origin. It's useful for content licensing requirements where specific countries can't access certain media.
Signed URLs are more granular. They contain an expiration timestamp, an IP restriction (optional), and a signature generated from a CloudFront key pair. The viewer must present a valid signed URL to access the content. Use signed URLs for individual file access (video streaming, document downloads) and signed cookies for access to multiple files under a path prefix (an entire members-only section of a site).
A common architecture: your application generates signed URLs or sets signed cookies after authentication, and CloudFront validates them at the edge without any request reaching your origin. This offloads authentication from your backend for static content, which can reduce origin costs significantly for content-heavy applications.