Skip to main content

CloudFront and CDN

Amazon CloudFront is AWS's global content delivery network (CDN) that caches and serves content from edge locations close to users. By distributing content across hundreds of points of presence worldwide, CloudFront reduces latency, improves performance, and provides DDoS protection and security features for web applications.

Overview

A CDN works by caching content at edge locations distributed globally. When a user requests content, the CDN routes the request to the nearest edge location. If the content is cached (a cache hit), it's served immediately from the edge. If not cached (a cache miss), the edge location fetches content from the origin server, caches it, and serves it to the user. Subsequent requests for the same content from nearby users are served from cache, dramatically reducing latency and origin load.

CloudFront integrates seamlessly with AWS services as origins: S3 buckets for static assets, Application Load Balancers for dynamic applications, API Gateway for APIs, and custom HTTP/HTTPS servers. This integration enables unified content delivery whether serving static files, dynamic API responses, or mixed workloads.

Understanding caching fundamentals is essential for effective CloudFront usage. Cache keys determine what constitutes unique content (URL path, query strings, headers, cookies), time-to-live (TTL) controls how long content stays cached, and cache behaviors define rules for different URL patterns. Proper cache configuration balances freshness (serving up-to-date content) against hit ratio (percentage of requests served from cache).

Core Principles

Global edge network: CloudFront operates over 450 edge locations across six continents, ensuring users worldwide receive content from nearby locations. This geographic distribution reduces latency from hundreds of milliseconds to tens of milliseconds for distant users.

Origin shield for efficiency: Origin Shield adds an additional caching layer between edge locations and origin servers, consolidating requests to reduce origin load. This is particularly valuable when using custom origins outside AWS that may have limited capacity or when origin fetches are expensive.

Cache optimization for cost and performance: Every byte served from cache instead of origin reduces both cost and latency. High cache hit ratios (80%+ for static content, 40-60%+ for dynamic) minimize origin requests, data transfer costs, and application load.

Security at the edge: CloudFront provides DDoS protection via AWS Shield Standard (automatic, free), SSL/TLS termination, geo-restriction, signed URLs/cookies for access control, and integration with AWS WAF for application-layer protection. Processing security at edge locations prevents malicious traffic from reaching origin infrastructure.

Edge computing capabilities: CloudFront Functions and Lambda@Edge execute code at edge locations, enabling request/response transformation, authentication, A/B testing, and dynamic content generation without origin requests. This moves computation closer to users for lower latency.

CloudFront Architecture

CloudFront distributions define how content is delivered. Each distribution has one or more origins (sources of content), cache behaviors (rules for caching different URL patterns), and edge location associations (geographic regions where content is cached).

When a user requests content, CloudFront routes to the nearest edge location based on network latency and geographic proximity. The edge location checks its cache. On a cache hit, content is served immediately. On a cache miss, the request flows through Origin Shield (if enabled) to the origin server. The origin response is cached at each layer before being returned to the user.

Request flow can be intercepted at multiple points for processing: CloudFront Functions execute on viewer requests/responses (lightweight transformations), while Lambda@Edge executes on viewer and origin requests/responses (complex logic, external API calls, dynamic content generation).

Distribution Types and Configuration

CloudFront offers web distributions for HTTP/HTTPS content delivery and RTMP distributions (deprecated, legacy streaming). Modern applications use web distributions for all content types including video streaming via HTTP-based protocols (HLS, DASH).

Creating a Distribution

A distribution requires at minimum an origin configuration and default cache behavior. Origins specify where CloudFront fetches content, while cache behaviors define how content is cached and delivered.

# Basic CloudFront distribution with S3 origin
resource "aws_cloudfront_distribution" "static_assets" {
enabled = true
is_ipv6_enabled = true
comment = "CDN for static assets"
default_root_object = "index.html"
price_class = "PriceClass_100" # US, Canada, Europe

origin {
domain_name = aws_s3_bucket.assets.bucket_regional_domain_name
origin_id = "S3-static-assets"
origin_access_control_id = aws_cloudfront_origin_access_control.main.id
}

default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "S3-static-assets"
viewer_protocol_policy = "redirect-to-https"
compress = true

min_ttl = 0
default_ttl = 86400 # 24 hours
max_ttl = 31536000 # 1 year

forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
}

restrictions {
geo_restriction {
restriction_type = "none"
}
}

viewer_certificate {
cloudfront_default_certificate = true
}

tags = {
Environment = "production"
ManagedBy = "terraform"
}
}

The default_cache_behavior applies to all requests that don't match more specific cache behaviors. Additional cache behaviors can target specific URL patterns (e.g., /api/* for API requests, /images/* for images).

price_class determines which edge locations CloudFront uses. PriceClass_100 (least expensive) uses only North America and Europe. PriceClass_200 adds Asia, Africa, and South America excluding the most expensive locations. PriceClass_All uses all edge locations globally for maximum performance at higher cost. Choose based on your user distribution and budget.

The compress option automatically compresses text-based content (HTML, CSS, JavaScript, JSON) using gzip or brotli before transmission, reducing transfer sizes by 60-80%. CloudFront determines whether to compress based on file type and size, applying compression transparently.

Origin Configuration

Origins define where CloudFront fetches content. CloudFront supports S3 buckets, load balancers, API Gateway endpoints, MediaStore/MediaPackage endpoints, and any custom HTTP/HTTPS server.

S3 Origins

S3 origins serve static assets like images, stylesheets, JavaScript, fonts, and downloadable files. Use Origin Access Control (OAC) to prevent direct S3 bucket access, ensuring all traffic flows through CloudFront.

# S3 bucket for static assets
resource "aws_s3_bucket" "assets" {
bucket = "example-static-assets"
}

# Block public access
resource "aws_s3_bucket_public_access_block" "assets" {
bucket = aws_s3_bucket.assets.id

block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

# Origin Access Control (OAC) - replaces legacy OAI
resource "aws_cloudfront_origin_access_control" "main" {
name = "s3-oac"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}

# S3 bucket policy allowing CloudFront via OAC
resource "aws_s3_bucket_policy" "assets" {
bucket = aws_s3_bucket.assets.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "AllowCloudFrontServicePrincipal"
Effect = "Allow"
Principal = {
Service = "cloudfront.amazonaws.com"
}
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.assets.arn}/*"
Condition = {
StringEquals = {
"AWS:SourceArn" = aws_cloudfront_distribution.static_assets.arn
}
}
}]
})
}

Origin Access Control (OAC) uses AWS Signature Version 4 to authenticate CloudFront requests to S3, replacing the legacy Origin Access Identity (OAI). OAC supports additional features including server-side encryption with AWS KMS, S3 bucket keys, and all S3 regions. Prefer OAC for all new distributions; migrate existing distributions from OAI to OAC.

By blocking public access and using OAC, you ensure content is only accessible via CloudFront, never directly from S3. This prevents users from bypassing CloudFront (and its caching, security features, logging) by discovering the S3 bucket URL. For comprehensive S3 patterns including versioning, lifecycle policies, and storage classes, see our file storage guide.

Custom Origins (ALB, API Gateway, External Servers)

Custom origins serve dynamic content from applications. CloudFront can cache dynamic responses based on cache keys (query strings, headers, cookies), reducing origin load even for personalized content.

resource "aws_cloudfront_distribution" "application" {
enabled = true
is_ipv6_enabled = true
comment = "CDN for application with multiple origins"

# S3 origin for static assets
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
}

# ALB origin for API
origin {
domain_name = aws_lb.api.dns_name
origin_id = "ALB-api"

custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
origin_keepalive_timeout = 60
origin_read_timeout = 60
}

custom_header {
name = "X-Custom-Header"
value = "CloudFrontOriginRequest"
}
}

# Default behavior for HTML and dynamic content
default_cache_behavior {
allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
cached_methods = ["GET", "HEAD", "OPTIONS"]
target_origin_id = "ALB-api"
viewer_protocol_policy = "redirect-to-https"
compress = true

min_ttl = 0
default_ttl = 0 # No caching by default for dynamic content
max_ttl = 0

forwarded_values {
query_string = true
headers = ["Authorization", "CloudFront-Viewer-Country"]
cookies {
forward = "all"
}
}
}

# Static assets cached from S3
ordered_cache_behavior {
path_pattern = "/assets/*"
allowed_methods = ["GET", "HEAD", "OPTIONS"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "S3-assets"
viewer_protocol_policy = "redirect-to-https"
compress = true

min_ttl = 0
default_ttl = 86400 # 24 hours
max_ttl = 31536000 # 1 year

forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
}

# API routes with custom caching
ordered_cache_behavior {
path_pattern = "/api/*"
allowed_methods = ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "ALB-api"
viewer_protocol_policy = "https-only"
compress = true

min_ttl = 0
default_ttl = 60 # 1 minute for API responses
max_ttl = 300 # 5 minutes max

forwarded_values {
query_string = true
headers = ["Authorization", "Accept", "Content-Type"]
cookies {
forward = "whitelist"
whitelisted_names = ["session-id"]
}
}
}

restrictions {
geo_restriction {
restriction_type = "none"
}
}

viewer_certificate {
acm_certificate_arn = aws_acm_certificate.main.arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2021"
}
}

ordered_cache_behavior entries are evaluated in order, matching the first pattern that applies. Order these from most specific to least specific. The default cache behavior acts as a catch-all for requests not matching any ordered behavior.

For custom origins, origin_protocol_policy should be https-only to encrypt traffic between CloudFront and origin. CloudFront to origin traffic traverses AWS's network (for ALB/API Gateway origins) or the internet (for external origins), so encryption is essential even though this is "backend" traffic.

Custom headers enable origin servers to distinguish CloudFront requests from direct requests, allowing you to block non-CloudFront traffic at the origin. Generate a secret header value and validate it at the origin application or load balancer level.

The origin_keepalive_timeout and origin_read_timeout values should be tuned based on origin response times. For fast APIs (sub-second responses), default values (5s keepalive, 30s read) suffice. For slow origins (video processing, batch operations), increase read timeout to prevent premature connection termination.

Origin Shield

Origin Shield adds a centralized caching layer between edge locations and origins. Rather than hundreds of edge locations independently requesting content from the origin, they request from Origin Shield. Origin Shield consolidates these into fewer origin requests, improving cache hit ratio at the origin shield layer and dramatically reducing origin load.

resource "aws_cloudfront_distribution" "with_origin_shield" {
enabled = true

origin {
domain_name = aws_lb.api.dns_name
origin_id = "ALB-with-shield"

custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}

origin_shield {
enabled = true
origin_shield_region = "us-east-1" # Choose region closest to origin
}
}

# ... cache behaviors
}

Choose an Origin Shield region geographically close to your origin to minimize latency between shield and origin. If your origin is an ALB in us-east-1, use us-east-1 for Origin Shield. For multi-region origins, enable Origin Shield in each region where you have origins.

Origin Shield adds cost ($0.005 per 10,000 requests) but reduces origin costs by decreasing requests, data transfer, and compute load. Calculate ROI by comparing Origin Shield costs against savings from reduced origin infrastructure costs and improved cache efficiency. Origin Shield typically pays for itself when origin fetches are expensive (complex computation, database queries, external API calls) or when cache hit ratio at edge locations is low due to geographic distribution of traffic.

Cache Behaviors and TTL Management

Cache behaviors define how CloudFront caches content. Each behavior specifies which methods to cache, how long to cache (TTL), what to include in the cache key (query strings, headers, cookies), and which origin to use.

Time-To-Live (TTL) Configuration

TTL determines how long content remains cached before CloudFront requests a fresh copy from the origin. CloudFront supports three TTL settings: minimum, default, and maximum.

The origin can specify TTL via HTTP cache headers (Cache-Control, Expires). CloudFront respects these headers within the min/max bounds defined in the cache behavior. If the origin specifies Cache-Control: max-age=3600 (1 hour) and your behavior has min_ttl=0, default_ttl=86400, max_ttl=31536000, CloudFront caches for 1 hour (respecting origin's preference). If the origin specifies no cache headers, CloudFront uses default_ttl.

# Cache behavior for static assets (long TTL)
resource "aws_cloudfront_distribution" "main" {
# ...

ordered_cache_behavior {
path_pattern = "/static/*"
target_origin_id = "S3-assets"

min_ttl = 3600 # Minimum 1 hour even if origin says less
default_ttl = 86400 # Default 24 hours if no origin cache headers
max_ttl = 31536000 # Maximum 1 year even if origin says more

forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
}

# Cache behavior for API responses (short TTL)
ordered_cache_behavior {
path_pattern = "/api/public/*"
target_origin_id = "ALB-api"

min_ttl = 0 # Allow origin to specify no caching
default_ttl = 60 # Default 1 minute
max_ttl = 300 # Maximum 5 minutes

forwarded_values {
query_string = true
headers = ["Accept", "Content-Type"]
cookies {
forward = "whitelist"
whitelisted_names = ["session-id"]
}
}
}
}

For static assets with versioned filenames (e.g., app.a1b2c3d4.js), use very long TTLs (1 year). When content changes, deploy with a new filename/version, and the old cached content becomes irrelevant. This maximizes cache hit ratio and minimizes origin load.

For dynamic content, balance freshness against cacheability. A 60-second TTL for API responses means users may see data up to 60 seconds old, but origin load reduces dramatically if multiple users request the same data within that window. For truly uncacheable content (user-specific data, transactional operations), set TTL to 0.

Origin cache headers override CloudFront TTL settings within the configured bounds. To fully control caching from CloudFront rather than origin, set min_ttl, default_ttl, and max_ttl to the same value, forcing that specific TTL regardless of origin headers.

Cache Key Configuration

The cache key determines what constitutes unique content. CloudFront creates a cache key from the URL path plus optionally query strings, headers, and cookies. Two requests with identical cache keys are served from the same cached object; different cache keys result in separate cached objects.

Minimizing cache key variability maximizes cache hit ratio. Include only the minimum necessary components in the cache key. For example, if your API response varies by Accept header but not by User-Agent, forward only Accept to ensure users with different browsers still share cached responses.

# Poor cache key: forwards all headers
forwarded_values {
query_string = true
headers = ["*"] # Every unique header combination = separate cache entry
cookies {
forward = "all" # Every unique cookie combination = separate cache entry
}
}
# Result: Very low cache hit ratio, high origin load

# Good cache key: forwards only necessary components
forwarded_values {
query_string = true # API responses vary by query parameters
headers = ["Accept", "Accept-Language"] # Only headers that affect response
cookies {
forward = "whitelist"
whitelisted_names = ["session-id"] # Only authentication cookie
}
}
# Result: Higher cache hit ratio, reduced origin load

Query string handling significantly impacts cache hit ratio. Setting query_string = true creates separate cache entries for every unique combination of query parameters, even if parameter order differs (?a=1&b=2 differs from ?b=2&a=1). For APIs where query parameter order doesn't matter, normalize parameters at the application level or use CloudFront Functions to sort query strings before caching.

For applications serving authenticated users, carefully consider cookie forwarding. Forwarding unique session cookies means each user gets separate cache entries, eliminating caching benefits. Instead, differentiate between authentication cookies (forward these) and tracking cookies (don't forward). Better yet, implement token-based authentication where the client includes tokens in Authorization headers, allowing more granular control over cache keys.

CloudFront provides cache policies (newer approach) as an alternative to forwarded_values (legacy approach). Cache policies offer more flexibility including query string and header allowlists/denylists, standardized configurations, and better defaults. Prefer cache policies for new distributions.

# Cache policy (recommended approach)
resource "aws_cloudfront_cache_policy" "api" {
name = "api-cache-policy"
min_ttl = 0
default_ttl = 60
max_ttl = 300

parameters_in_cache_key_and_forwarded_to_origin {
cookies_config {
cookie_behavior = "whitelist"
cookies {
items = ["session-id"]
}
}

headers_config {
header_behavior = "whitelist"
headers {
items = ["Accept", "Accept-Language", "Authorization"]
}
}

query_strings_config {
query_string_behavior = "all"
}

enable_accept_encoding_gzip = true
enable_accept_encoding_brotli = true
}
}

# Use cache policy in cache behavior
resource "aws_cloudfront_distribution" "main" {
# ...

default_cache_behavior {
target_origin_id = "origin-id"
viewer_protocol_policy = "redirect-to-https"
cache_policy_id = aws_cloudfront_cache_policy.api.id
}
}

Cache Invalidation and Management

Cached content remains at edge locations until TTL expires. To force immediate removal (when deploying critical fixes or updating content before TTL expires), use cache invalidation.

# Cache invalidation via Terraform
resource "null_resource" "invalidate_cache" {
triggers = {
# Invalidate whenever static assets change
assets_hash = filemd5("${path.module}/../static/app.js")
}

provisioner "local-exec" {
command = <<-EOT
aws cloudfront create-invalidation \
--distribution-id ${aws_cloudfront_distribution.main.id} \
--paths "/app.js" "/css/*"
EOT
}
}

Invalidations propagate globally within minutes, removing specified content from all edge locations. CloudFront allows 1,000 free invalidation paths per month; additional paths cost $0.005 per path.

Wildcard invalidations (/*, /images/*) count as single paths but take longer to complete than specific file invalidations. For large-scale cache clearing, consider versioning files instead of invalidating. Deploy app.v2.js rather than invalidating app.js, allowing gradual rollout and instant rollback (revert HTML to reference app.v1.js).

Cache invalidations are eventually consistent and may take 10-15 minutes to fully propagate globally. During this window, some users receive old cached content while others receive new content. For zero-downtime deployments requiring immediate consistency, use versioned assets or blue-green deployment at the origin with CloudFront pointing to the new origin.

Alternative to invalidation: configure origin cache headers to prevent caching (Cache-Control: no-cache, no-store, must-revalidate). CloudFront still fetches from origin on every request, but the request flows through CloudFront infrastructure, providing DDoS protection, SSL termination, and geographic routing even without caching benefits.

SSL/TLS and Custom Domains

CloudFront provides HTTPS by default using *.cloudfront.net domains. For custom domains, configure SSL certificates and DNS records.

Custom Domain Configuration

Custom domains require SSL/TLS certificates from AWS Certificate Manager (ACM) in the us-east-1 region specifically. CloudFront only recognizes certificates in this region regardless of where your distribution or origins reside.

# ACM certificate in us-east-1 (required for CloudFront)
provider "aws" {
alias = "us_east_1"
region = "us-east-1"
}

resource "aws_acm_certificate" "cloudfront" {
provider = aws.us_east_1
domain_name = "cdn.example.com"
validation_method = "DNS"

subject_alternative_names = [
"*.cdn.example.com"
]

lifecycle {
create_before_destroy = true
}
}

# Route 53 validation records
resource "aws_route53_record" "cert_validation" {
for_each = {
for dvo in aws_acm_certificate.cloudfront.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
}

allow_overwrite = true
name = each.value.name
records = [each.value.record]
ttl = 60
type = each.value.type
zone_id = data.aws_route53_zone.main.zone_id
}

resource "aws_acm_certificate_validation" "cloudfront" {
provider = aws.us_east_1
certificate_arn = aws_acm_certificate.cloudfront.arn
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
}

# CloudFront distribution with custom domain
resource "aws_cloudfront_distribution" "custom_domain" {
enabled = true

aliases = ["cdn.example.com"]

# ... origins and cache behaviors

viewer_certificate {
acm_certificate_arn = aws_acm_certificate.cloudfront.arn
ssl_support_method = "sni-only"
minimum_protocol_version = "TLSv1.2_2021"
}
}

# Route 53 alias record pointing to CloudFront
resource "aws_route53_record" "cdn" {
zone_id = data.aws_route53_zone.main.zone_id
name = "cdn.example.com"
type = "A"

alias {
name = aws_cloudfront_distribution.custom_domain.domain_name
zone_id = aws_cloudfront_distribution.custom_domain.hosted_zone_id
evaluate_target_health = false
}
}

The aliases field lists custom domain names for the distribution. These must match the certificate's domain name or subject alternative names. CloudFront validates this match and rejects distributions where aliases don't match the certificate.

ssl_support_method = "sni-only" uses Server Name Indication, the modern TLS extension supported by all browsers since 2010. This is free. The alternative vip dedicates IP addresses for the distribution at $600/month, only necessary for legacy clients that don't support SNI (essentially none in current usage).

minimum_protocol_version determines the oldest TLS version accepted. TLSv1.2_2021 (TLS 1.2 and newer) balances security and compatibility. For maximum security, use TLSv1.3_2021 (TLS 1.3 only), though this may exclude older clients. Never use TLS 1.0 or 1.1, which have known vulnerabilities.

For multi-domain distributions serving multiple sites, include all domains in aliases and the certificate's subject alternative names. CloudFront serves the appropriate content based on the Host header in requests.

For comprehensive DNS configuration patterns including Route 53 health checks and failover, see our Route 53 guide.

Security Features

CloudFront provides multiple security layers protecting both content and applications.

Origin Access Control for S3

Origin Access Control (OAC) ensures S3 buckets are only accessible via CloudFront, never directly. This prevents users from bypassing CloudFront by discovering bucket URLs, ensuring all requests benefit from CloudFront's caching, DDoS protection, logging, and access controls.

# S3 bucket with private access only
resource "aws_s3_bucket" "private_content" {
bucket = "example-private-content"
}

resource "aws_s3_bucket_public_access_block" "private_content" {
bucket = aws_s3_bucket.private_content.id

block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

# Origin Access Control
resource "aws_cloudfront_origin_access_control" "s3_oac" {
name = "s3-oac"
origin_access_control_origin_type = "s3"
signing_behavior = "always"
signing_protocol = "sigv4"
}

# CloudFront distribution using OAC
resource "aws_cloudfront_distribution" "secure_s3" {
enabled = true

origin {
domain_name = aws_s3_bucket.private_content.bucket_regional_domain_name
origin_id = "S3-private"
origin_access_control_id = aws_cloudfront_origin_access_control.s3_oac.id
}

# ... cache behaviors
}

# S3 bucket policy allowing only CloudFront via OAC
resource "aws_s3_bucket_policy" "private_content" {
bucket = aws_s3_bucket.private_content.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Sid = "AllowCloudFrontServicePrincipal"
Effect = "Allow"
Principal = {
Service = "cloudfront.amazonaws.com"
}
Action = "s3:GetObject"
Resource = "${aws_s3_bucket.private_content.arn}/*"
Condition = {
StringEquals = {
"AWS:SourceArn" = aws_cloudfront_distribution.secure_s3.arn
}
}
}]
})
}

OAC uses AWS Signature Version 4 to authenticate CloudFront requests, with the bucket policy validating the distribution ARN. This ensures only this specific distribution can access the bucket, preventing other distributions or services from using OAC to gain access.

Signed URLs and Signed Cookies

Signed URLs and signed cookies provide time-limited access to content, enabling secure delivery of premium content, per-user content restrictions, or temporary access grants.

Signed URLs embed access controls in the URL itself. Each URL includes an expiration timestamp and cryptographic signature. CloudFront validates signatures before serving content, rejecting expired or invalid signatures.

// TypeScript: Generate signed URL using AWS SDK
import { getSignedUrl } from '@aws-sdk/cloudfront-signer';

function generateSignedUrl(objectKey: string, expirationMinutes: number): string {
const url = `https://cdn.example.com/${objectKey}`;
const privateKey = process.env.CLOUDFRONT_PRIVATE_KEY!;
const keyPairId = process.env.CLOUDFRONT_KEY_PAIR_ID!;

const dateLessThan = new Date(Date.now() + expirationMinutes * 60 * 1000);

return getSignedUrl({
url,
keyPairId,
dateLessThan: dateLessThan.toISOString(),
privateKey,
});
}

// Usage: Generate URL valid for 1 hour
const signedUrl = generateSignedUrl('videos/premium-content.mp4', 60);

Signed cookies provide similar access control but via cookies instead of URL parameters. This is preferable when generating signed URLs for every resource is impractical (HLS video with hundreds of segment files) or when you don't want access tokens visible in URLs.

// TypeScript: Set signed cookies for CloudFront
import { getSignedCookies } from '@aws-sdk/cloudfront-signer';

function setSignedCookies(res: Response, resourcePath: string): void {
const privateKey = process.env.CLOUDFRONT_PRIVATE_KEY!;
const keyPairId = process.env.CLOUDFRONT_KEY_PAIR_ID!;

const dateLessThan = new Date(Date.now() + 3600 * 1000); // 1 hour

const cookies = getSignedCookies({
url: `https://cdn.example.com/${resourcePath}*`, // Wildcard for multiple files
keyPairId,
dateLessThan: dateLessThan.toISOString(),
privateKey,
});

// Set cookies in response
res.cookie('CloudFront-Policy', cookies['CloudFront-Policy'], {
httpOnly: true,
secure: true,
sameSite: 'strict'
});
res.cookie('CloudFront-Signature', cookies['CloudFront-Signature'], {
httpOnly: true,
secure: true,
sameSite: 'strict'
});
res.cookie('CloudFront-Key-Pair-Id', cookies['CloudFront-Key-Pair-Id'], {
httpOnly: true,
secure: true,
sameSite: 'strict'
});
}

To use signed URLs or cookies, configure CloudFront with trusted key groups (newer approach) or trusted signers (legacy approach). Key groups allow rotating keys without updating distributions.

# Public key for signing
resource "aws_cloudfront_public_key" "signing_key" {
encoded_key = file("${path.module}/cloudfront_public_key.pem")
name = "cloudfront-signing-key"
}

# Key group containing public keys
resource "aws_cloudfront_key_group" "main" {
name = "cloudfront-key-group"

items = [aws_cloudfront_public_key.signing_key.id]
}

# Distribution configured to require signed URLs/cookies
resource "aws_cloudfront_distribution" "signed" {
enabled = true

# ... origins

default_cache_behavior {
target_origin_id = "S3-private"
viewer_protocol_policy = "redirect-to-https"

trusted_key_groups = [aws_cloudfront_key_group.main.id]

# ... other settings
}
}

Generate the private/public key pair using OpenSSL:

# Generate 2048-bit RSA key pair
openssl genrsa -out private_key.pem 2048
openssl rsa -pubout -in private_key.pem -out public_key.pem

# Store private key securely (AWS Secrets Manager, environment variable)
# Upload public key to CloudFront via Terraform

Store private keys securely using AWS Secrets Manager or environment variables, never committing them to source control. Rotate keys periodically (annually or when compromised) by generating new key pairs, adding to the key group, updating applications to sign with new keys, then removing old keys after all URLs/cookies using them expire. See our secrets management guide for key rotation patterns.

Geographic Restrictions

Geographic restrictions (geo-blocking) prevent users in specific countries from accessing content, useful for content licensing restrictions or compliance requirements.

resource "aws_cloudfront_distribution" "geo_restricted" {
enabled = true

# ... origins and cache behaviors

# Block access from specific countries
restrictions {
geo_restriction {
restriction_type = "blacklist"
locations = ["RU", "CN", "KP"] # ISO 3166-1 alpha-2 codes
}
}

# Or allow only specific countries
# restrictions {
# geo_restriction {
# restriction_type = "whitelist"
# locations = ["US", "CA", "GB", "DE", "FR"]
# }
# }
}

CloudFront determines user location from their IP address. Users from blocked countries receive a 403 Forbidden response. Geographic restrictions apply at the distribution level, affecting all content served by that distribution.

For more granular control (different restrictions for different content), create separate distributions or use Lambda@Edge to implement custom geographic logic based on the CloudFront-Viewer-Country header.

AWS WAF Integration

AWS Web Application Firewall (WAF) protects against common web exploits (SQL injection, cross-site scripting, bot traffic) at the CloudFront edge. WAF rules execute before requests reach origins, blocking malicious traffic.

# WAF Web ACL
resource "aws_wafv2_web_acl" "cloudfront" {
name = "cloudfront-waf"
scope = "CLOUDFRONT" # Must be CLOUDFRONT for CloudFront distributions

default_action {
allow {}
}

# AWS managed rule set for common threats
rule {
name = "AWSManagedRulesCommonRuleSet"
priority = 1

override_action {
none {}
}

statement {
managed_rule_group_statement {
vendor_name = "AWS"
name = "AWSManagedRulesCommonRuleSet"
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "AWSManagedRulesCommonRuleSetMetric"
sampled_requests_enabled = true
}
}

# Rate limiting rule
rule {
name = "RateLimitRule"
priority = 2

action {
block {}
}

statement {
rate_based_statement {
limit = 2000 # Requests per 5 minutes per IP
aggregate_key_type = "IP"
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "RateLimitMetric"
sampled_requests_enabled = true
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "CloudFrontWAF"
sampled_requests_enabled = true
}
}

# Attach WAF to CloudFront distribution
resource "aws_cloudfront_distribution" "protected" {
enabled = true

web_acl_id = aws_wafv2_web_acl.cloudfront.arn

# ... origins and cache behaviors
}

WAF costs include Web ACL fees ($5/month), per-rule fees ($1/month per rule), and request fees ($0.60 per million requests). AWS managed rule sets provide pre-configured protection against OWASP Top 10 vulnerabilities, bot traffic, and known bad inputs at reasonable costs given the development effort saved.

Rate limiting rules protect against abuse and DDoS attacks by limiting requests per IP address. Tune rate limits based on legitimate traffic patterns: too low blocks genuine users, too high allows abuse. Monitor WAF metrics to identify appropriate thresholds.

For comprehensive security patterns including input validation, authentication, and authorization, see our security guides.

Edge Computing: CloudFront Functions vs Lambda@Edge

CloudFront executes code at edge locations to manipulate requests and responses. Two options exist: CloudFront Functions (lightweight, sub-millisecond execution) and Lambda@Edge (full Lambda power, millisecond execution).

CloudFront Functions

CloudFront Functions execute in under 1 millisecond at CloudFront's 450+ edge locations. They handle simple transformations: URL rewrites, header manipulation, request validation, and response customization. CloudFront Functions use a JavaScript runtime (ECMAScript 5.1 compatible) with strict limits: 2 MB code size, 1 MB memory, sub-millisecond execution.

// CloudFront Function: Add security headers to responses
function handler(event) {
var response = event.response;
var headers = response.headers;

// Add security headers
headers['strict-transport-security'] = {
value: 'max-age=63072000; includeSubdomains; preload'
};
headers['x-content-type-options'] = { value: 'nosniff' };
headers['x-frame-options'] = { value: 'DENY' };
headers['x-xss-protection'] = { value: '1; mode=block' };
headers['referrer-policy'] = { value: 'strict-origin-when-cross-origin' };

return response;
}
// CloudFront Function: URL rewrite for SPA routing
function handler(event) {
var request = event.request;
var uri = request.uri;

// Rewrite /users/123 to /index.html for client-side routing
if (!uri.includes('.') && !uri.endsWith('/')) {
request.uri = '/index.html';
}

// Add index.html to directory requests
if (uri.endsWith('/')) {
request.uri += 'index.html';
}

return request;
}

Deploy CloudFront Functions via Terraform:

resource "aws_cloudfront_function" "security_headers" {
name = "security-headers"
runtime = "cloudfront-js-1.0"
comment = "Add security headers to all responses"
publish = true

code = file("${path.module}/functions/security-headers.js")
}

resource "aws_cloudfront_distribution" "main" {
enabled = true

# ... origins

default_cache_behavior {
target_origin_id = "S3-origin"
viewer_protocol_policy = "redirect-to-https"

# Attach function to viewer response
function_association {
event_type = "viewer-response"
function_arn = aws_cloudfront_function.security_headers.arn
}
}
}

CloudFront Functions cost $0.10 per million invocations, dramatically cheaper than Lambda@Edge ($0.60 per million requests plus compute duration charges). Use CloudFront Functions for simple transformations to minimize costs.

Limitations: CloudFront Functions cannot make network requests (no external API calls), cannot access request body, cannot read/write files, and have no access to environment variables or AWS SDK. For these requirements, use Lambda@Edge.

Lambda@Edge

Lambda@Edge executes full Lambda functions at CloudFront's regional edge caches (fewer locations than CloudFront Functions, but still globally distributed). Lambda@Edge supports larger code sizes (50 MB), longer execution (30 seconds origin request/response, 5 seconds viewer request/response), network requests, and full Node.js or Python runtimes.

// Lambda@Edge: Authentication and request signing
import { CloudFrontRequestHandler } from 'aws-lambda';
import * as AWS from 'aws-sdk';

export const handler: CloudFrontRequestHandler = async (event) => {
const request = event.Records[0].cf.request;
const headers = request.headers;

// Extract and validate JWT token
const authHeader = headers['authorization']?.[0]?.value;

if (!authHeader || !authHeader.startsWith('Bearer ')) {
return {
status: '401',
statusDescription: 'Unauthorized',
body: 'Missing or invalid authorization header',
};
}

const token = authHeader.substring(7);

try {
// Validate JWT (simplified - use proper JWT library in production)
const payload = validateJWT(token);

// Add user context to custom header for origin
headers['x-user-id'] = [{ key: 'X-User-Id', value: payload.sub }];
headers['x-user-role'] = [{ key: 'X-User-Role', value: payload.role }];

return request;
} catch (error) {
return {
status: '403',
statusDescription: 'Forbidden',
body: 'Invalid token',
};
}
};

function validateJWT(token: string): any {
// Implement JWT validation logic
// Verify signature, expiration, issuer, etc.
throw new Error('Not implemented');
}
# Lambda@Edge: Generate dynamic content
import json

def lambda_handler(event, context):
request = event['Records'][0]['cf']['request']

# Generate personalized content based on geolocation
headers = request['headers']
country = headers.get('cloudfront-viewer-country', [{}])[0].get('value', 'Unknown')

# Create JSON response
response_body = json.dumps({
'message': f'Hello from {country}!',
'timestamp': context.request_time,
'edge_location': headers.get('cloudfront-viewer-pop', [{}])[0].get('value', 'Unknown')
})

return {
'status': '200',
'statusDescription': 'OK',
'headers': {
'content-type': [{'key': 'Content-Type', 'value': 'application/json'}],
'cache-control': [{'key': 'Cache-Control', 'value': 'max-age=60'}]
},
'body': response_body
}

Deploy Lambda@Edge functions in the us-east-1 region (required for CloudFront association), then associate with CloudFront distributions:

# Lambda@Edge function must be in us-east-1
provider "aws" {
alias = "us_east_1"
region = "us-east-1"
}

# IAM role for Lambda@Edge
resource "aws_iam_role" "lambda_edge" {
provider = aws.us_east_1
name = "lambda-edge-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Principal = {
Service = [
"lambda.amazonaws.com",
"edgelambda.amazonaws.com"
]
}
Action = "sts:AssumeRole"
}]
})
}

resource "aws_iam_role_policy_attachment" "lambda_edge" {
provider = aws.us_east_1
role = aws_iam_role.lambda_edge.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

# Lambda function
resource "aws_lambda_function" "edge_auth" {
provider = aws.us_east_1
filename = "edge-auth.zip"
function_name = "cloudfront-edge-auth"
role = aws_iam_role.lambda_edge.arn
handler = "index.handler"
runtime = "nodejs18.x"
publish = true # Must publish for Lambda@Edge
timeout = 5

source_code_hash = filebase64sha256("edge-auth.zip")
}

# Associate with CloudFront
resource "aws_cloudfront_distribution" "main" {
enabled = true

# ... origins

default_cache_behavior {
target_origin_id = "origin"
viewer_protocol_policy = "redirect-to-https"

lambda_function_association {
event_type = "viewer-request"
lambda_arn = aws_lambda_function.edge_auth.qualified_arn # Use qualified ARN with version
include_body = false
}
}
}

Lambda@Edge costs include request charges ($0.60 per million) and compute duration charges based on memory and execution time. A 128 MB function executing for 50ms costs ~$0.0000009 per invocation. Monitor CloudWatch metrics to track costs and optimize memory/duration settings.

Lambda@Edge limitations: cannot use environment variables (use Systems Manager Parameter Store instead), limited execution time (5s viewer events, 30s origin events), no VPC access, and function must be in us-east-1. Despite these constraints, Lambda@Edge enables sophisticated edge computing for authentication, personalization, A/B testing, and dynamic content generation.

Choose CloudFront Functions for simple transformations (header manipulation, URL rewrites, basic validation) due to lower cost and latency. Choose Lambda@Edge for complex logic requiring external requests, longer execution, or language features beyond ECMAScript 5.1.

Performance Optimization

Optimizing CloudFront performance requires maximizing cache hit ratio, minimizing origin requests, and reducing response sizes.

Cache Hit Ratio Optimization

Cache hit ratio is the percentage of requests served from cache. Higher ratios reduce origin load, latency, and costs. Target 80%+ for static content, 40-60%+ for dynamic content.

Strategies to improve cache hit ratio:

Normalize cache keys: Minimize cache key variability by forwarding only necessary query parameters, headers, and cookies. Use CloudFront Functions to normalize or sort query strings before cache lookup.

Longer TTLs for static content: Set 1-year TTLs for versioned assets (app.v123.js). When content changes, deploy with new versions rather than invalidating cache.

Separate distributions or cache behaviors: Create separate behaviors for static (long TTL) and dynamic (short TTL) content. This prevents dynamic content cache misses from diluting overall hit ratio metrics.

Enable Origin Shield: Origin Shield consolidates requests from edge locations, improving effective cache hit ratio at the shield layer even when edge hit ratio is low due to geographic traffic distribution.

Compress responses: Enable compress = true in cache behaviors. CloudFront caches both compressed and uncompressed versions based on Accept-Encoding headers, serving appropriate versions to clients.

Monitor cache hit ratio in CloudFront metrics:

# CloudWatch alarm for low cache hit ratio
resource "aws_cloudwatch_metric_alarm" "low_cache_hit_ratio" {
alarm_name = "cloudfront-low-cache-hit-ratio"
comparison_operator = "LessThanThreshold"
evaluation_periods = 2
metric_name = "CacheHitRate"
namespace = "AWS/CloudFront"
period = 300
statistic = "Average"
threshold = 70 # Alert if hit ratio drops below 70%

dimensions = {
DistributionId = aws_cloudfront_distribution.main.id
}

alarm_description = "CloudFront cache hit ratio has dropped below 70%"
}

Analyze CloudWatch Logs to identify cacheable content being cache-missed. Look for patterns: are query parameters unnecessarily varying? Are cookies preventing caching? Are TTLs too short?

Compression and File Optimization

CloudFront automatically compresses text-based files (HTML, CSS, JavaScript, JSON, XML) when compress = true in cache behaviors. Compression reduces transfer sizes by 60-80%, improving load times and reducing data transfer costs.

CloudFront only compresses responses meeting criteria:

  • File size between 1 KB and 10 MB
  • Content-Type indicates text-based content
  • Client sends Accept-Encoding: gzip or Accept-Encoding: br (Brotli)
  • Response doesn't already have Content-Encoding

For images, compress at the source rather than relying on CloudFront. Use modern formats (WebP, AVIF) for photos, SVG for icons, and appropriate quality settings. Tools like ImageMagick, Sharp, or image CDN services optimize images during build or upload.

# Optimize images during build
npm install sharp

# optimize-images.js
const sharp = require('sharp');

async function optimizeImage(input, output) {
await sharp(input)
.resize(1920, 1080, { fit: 'inside', withoutEnlargement: true })
.webp({ quality: 85 })
.toFile(output);
}

Origin Request Optimization

Minimize origin requests to reduce costs and improve performance:

Connection pooling: CloudFront maintains persistent connections to origins, reusing TCP/TLS connections across requests. Configure origin_keepalive_timeout appropriately (default 5 seconds) to balance connection reuse against origin connection pool limits.

HTTP/2 to origin: Enable HTTP/2 between CloudFront and ALB/API Gateway origins for multiplexing multiple requests over single connections. Configure ALB to support HTTP/2.

Conditional requests: CloudFront sends If-Modified-Since and If-None-Match headers to origins, enabling 304 Not Modified responses when content hasn't changed. This reduces data transfer even on cache misses.

Origin Shield: As discussed earlier, Origin Shield consolidates edge location requests, reducing origin load by 30-50% even when edge cache hit ratio is modest.

For comprehensive performance optimization patterns including database optimization, async processing, and caching strategies, see our performance guides.

Monitoring and Logging

CloudFront provides multiple monitoring and logging capabilities for visibility into distribution behavior.

CloudWatch Metrics

CloudFront automatically publishes metrics to CloudWatch:

  • Requests: Total request count
  • BytesDownloaded, BytesUploaded: Data transfer volume
  • 4xxErrorRate, 5xxErrorRate: Error rates
  • CacheHitRate: Percentage of cache hits
# CloudWatch dashboard for CloudFront
resource "aws_cloudwatch_dashboard" "cloudfront" {
dashboard_name = "cloudfront-monitoring"

dashboard_body = jsonencode({
widgets = [
{
type = "metric"
properties = {
metrics = [
["AWS/CloudFront", "Requests", { stat = "Sum", label = "Total Requests" }],
[".", "CacheHitRate", { stat = "Average", label = "Cache Hit Rate %" }]
]
period = 300
region = "us-east-1"
title = "CloudFront Requests and Cache Performance"
}
},
{
type = "metric"
properties = {
metrics = [
["AWS/CloudFront", "4xxErrorRate", { stat = "Average" }],
[".", "5xxErrorRate", { stat = "Average" }]
]
period = 300
region = "us-east-1"
title = "CloudFront Error Rates"
}
}
]
})
}

Access Logs

CloudFront access logs record every request with details including timestamp, edge location, client IP, request path, status code, user agent, and more. Enable logging to S3 for analysis and troubleshooting.

# S3 bucket for access logs
resource "aws_s3_bucket" "cloudfront_logs" {
bucket = "example-cloudfront-logs"
}

resource "aws_s3_bucket_lifecycle_configuration" "cloudfront_logs" {
bucket = aws_s3_bucket.cloudfront_logs.id

rule {
id = "delete-old-logs"
status = "Enabled"

expiration {
days = 90 # Delete logs after 90 days
}

transition {
days = 30
storage_class = "STANDARD_IA" # Move to cheaper storage after 30 days
}
}
}

# Enable access logging on distribution
resource "aws_cloudfront_distribution" "logged" {
enabled = true

# ... origins and cache behaviors

logging_config {
include_cookies = false
bucket = aws_s3_bucket.cloudfront_logs.bucket_domain_name
prefix = "cloudfront/"
}
}

Logs are delivered within hours of requests, not in real-time. For real-time monitoring, use CloudWatch metrics or real-time logs (Kinesis Data Streams).

Analyze access logs using Athena for SQL queries over log data:

-- Athena query: Top requested URLs
SELECT
request_uri,
COUNT(*) as request_count
FROM cloudfront_logs
WHERE date >= '2024-01-01'
GROUP BY request_uri
ORDER BY request_count DESC
LIMIT 20;

-- Cache hit analysis
SELECT
result_type,
COUNT(*) as count,
COUNT(*) * 100.0 / SUM(COUNT(*)) OVER () as percentage
FROM cloudfront_logs
WHERE date >= '2024-01-01'
GROUP BY result_type;

For comprehensive observability patterns including distributed tracing and log aggregation, see our observability guides.

Cost Optimization

CloudFront costs include data transfer out (varying by region), HTTP/HTTPS requests, invalidation requests, and optional features (field-level encryption, real-time logs).

Price Classes

Price classes control which edge locations CloudFront uses, directly impacting costs. Lower price classes exclude expensive regions, reducing data transfer costs at the expense of performance for users in those regions.

  • PriceClass_100: US, Canada, Europe (~lowest cost)
  • PriceClass_200: PriceClass_100 + Asia, Africa, South America (except most expensive locations)
  • PriceClass_All: All edge locations globally (highest performance, highest cost)
resource "aws_cloudfront_distribution" "cost_optimized" {
enabled = true
price_class = "PriceClass_100" # Reduce costs if users primarily in NA/EU

# ... origins and cache behaviors
}

Analyze your user distribution. If 95% of users are in North America and Europe, PriceClass_100 saves 20-40% on data transfer while impacting only 5% of users. For truly global applications, PriceClass_All provides best experience.

Cache Efficiency

Higher cache hit ratios reduce origin data transfer, origin compute costs, and CloudFront origin-fetch data transfer charges. Improving cache hit ratio from 60% to 80% cuts origin traffic in half.

Monitor and optimize cache efficiency:

  • Set appropriate TTLs (longer for static content)
  • Minimize cache key variability
  • Enable Origin Shield to reduce origin requests
  • Use versioned assets to enable long TTLs

Compression

Enabling compression reduces data transfer costs by 60-80% for text content. CloudFront charges for compressed bytes transferred, not original file sizes, making compression a pure cost savings with no downside.

Regional Edge Caches and Origin Shield

Regional edge caches (automatically used by CloudFront) add an intermediate caching layer between edge locations and origins. This improves cache hit ratio for less popular content.

Origin Shield adds explicit consolidation of origin requests. While Origin Shield adds cost ($0.005 per 10,000 requests), it typically saves more in reduced origin costs (compute, data transfer, database queries).

Free Tier

CloudFront free tier (12 months for new AWS accounts) includes:

  • 1 TB data transfer out per month
  • 10 million HTTP/HTTPS requests per month
  • 2 million CloudFront Function invocations per month

This covers substantial traffic for small to medium applications, making CloudFront cost-effective even for startups and side projects.

Anti-Patterns and Common Mistakes

Public S3 buckets instead of OAC: Serving content directly from public S3 buckets bypasses CloudFront's caching, security, and performance benefits. Always use CloudFront with OAC for S3 content delivery. See our file storage guide for S3 best practices.

Over-invalidating cache: Frequent invalidations (multiple times per hour) indicate architectural issues. Use versioned assets for static content and appropriate TTLs for dynamic content instead of relying on invalidations. Invalidations are for emergency fixes, not regular deployment workflows.

Forwarding all headers/cookies: Including all headers and cookies in cache keys destroys cache hit ratio. Forward only headers/cookies that actually affect responses. Use cache policies to explicitly control cache key composition.

Ignoring cache hit ratio: Deploying CloudFront without monitoring cache hit ratio wastes potential performance and cost benefits. Low cache hit ratio (below 40-50%) indicates configuration issues requiring investigation.

Not enabling compression: Disabling compression needlessly increases data transfer costs and user load times. Always enable compression unless you have specific reasons not to (pre-compressed content, binary files that don't compress).

Using CloudFront for non-cacheable content only: While CloudFront provides value even without caching (DDoS protection, SSL termination, global routing), it's most cost-effective when caching meaningful portions of traffic. If cache hit ratio is near 0%, evaluate whether CloudFront is the right solution.

Hardcoding cloudfront.net URLs: Using default CloudFront domain names (d123456.cloudfront.net) in production loses branding and complicates future migrations. Always use custom domains with SSL certificates.

Not implementing cache-control headers: Letting CloudFront determine caching purely from distribution settings limits flexibility. Implement proper Cache-Control headers at origins to control caching per-response based on content type and freshness requirements.

Overly complex Lambda@Edge functions: Lambda@Edge adds latency and cost to every request. Keep edge functions simple and fast. For complex processing, handle at the origin application instead.

For comprehensive architectural patterns avoiding common pitfalls, see our API design guide and microservices architecture.

Integration with Other AWS Services

CloudFront integrates deeply with AWS services for comprehensive content delivery:

  • S3: Primary use case for static asset delivery with OAC security. See our file storage guide for comprehensive S3 patterns including lifecycle policies, versioning, and storage classes.

  • Application Load Balancer: Distribute dynamic application traffic globally with caching. CloudFront caches appropriate responses (GET requests with cache headers) while forwarding uncacheable traffic to ALB. See our networking guide.

  • API Gateway: Add CloudFront in front of API Gateway for caching, custom domains, and DDoS protection. API Gateway caching can complement CloudFront caching for multi-layer optimization. See our API Gateway guide.

  • Lambda@Edge and CloudFront Functions: Execute code at edge locations for authentication, personalization, A/B testing, and request/response transformation.

  • Route 53: Custom domains require Route 53 (or external DNS) alias records pointing to CloudFront distributions. Route 53 provides health checks and failover for multi-origin configurations. See our Route 53 guide.

  • ACM: Free SSL/TLS certificates for custom domains with automated renewal. Certificates must be in us-east-1 for CloudFront usage.

  • WAF: Protect applications from web exploits and bot traffic at the edge before requests reach origins. See our security guide.

  • Shield: DDoS protection integrated automatically (Shield Standard) or enhanced (Shield Advanced for additional cost).

Further Reading

AWS Documentation:

CDN Concepts:

Related Documentation: