Filter concepts by levelShowing all levels.

AWS · Section 21

CloudFront and Edge Delivery

Level
intermediate
Read
20 min
Concepts
3

A CloudFront distribution routes requests through worldwide edge locations to one or more origins, using path-matched cache behaviors that each carry a cache policy (what varies the cache key) and an origin request policy (what is forwarded on a miss). This section covers that vocabulary, the two dominant origin patterns — S3 behind Origin Access Control for static content, ALB/API Gateway for dynamic content — and how TTL, invalidation, and versioned filenames interact to keep cached content fresh without unnecessary cost or cache gaps.

This section

What is true here

  1. A cache policy controls the cache key; an origin request policy controls what is forwarded to the origin — distinct settings, easily confused.
  2. Every extra dimension in the cache key multiplies distinct cache entries — keep it as narrow as the content actually requires.
  3. CloudFront + S3 via Origin Access Control keeps the bucket private; CloudFront + ALB/API Gateway still adds TLS termination and DDoS absorption even at a low cache hit rate.
  4. Default TTL is 24 hours with no origin caching headers.
  5. Invalidation costs money and never reaches caches outside CloudFront — AWS recommends versioned filenames for frequently-updated content instead.

What you will be able to do

  • Explain the distinction between a cache policy and an origin request policy
  • Design a cache key narrow enough to preserve a high hit rate
  • Choose the right origin pattern (S3+OAC vs ALB/API Gateway) for a given content type
  • Decide between invalidation and versioned filenames for keeping content fresh
From the CloudFront model to keeping content fresh
fetches fromkeptcurrent via

Distribution, origin, cache behavior

S3 (static) or ALB (dynamic)

TTL, invalidation, versioned names

  • Distribution, origin, cache behavior
    • leads to S3 (static) or ALB (dynamic) (fetches from)
  • S3 (static) or ALB (dynamic)
    • leads to TTL, invalidation, versioned names (kept current via)
  • TTL, invalidation, versioned names

CloudFront and Edge Delivery

The distribution/origin/cache-behavior vocabulary, S3 vs ALB origin patterns, and cache keys/TTL/invalidation trade-offs.

CloudFront Core Vocabulary

coreintermediate

A distribution tells CloudFront where to get content (the origin — S3, an ALB, a custom HTTP server) and how to cache and route it. Cache behaviors match request paths to specific settings; a cache policy controls what varies the cache key (headers, cookies, query strings); an origin request policy controls what gets forwarded to the origin. Edge locations are the worldwide points of presence that actually cache and serve content close to viewers.

Think of it as

A distribution is a franchise agreement — it defines where the "central kitchen" (origin) is and the rules every local branch (edge location) follows for what to keep on hand and how long, so most orders never have to travel back to the kitchen at all.

What we're doing: See how a wide cache key silently collapses a cache's effective hit rate.

cache-key-width.txttext
Cache policy includes the "Authorization" header in the cache key
→ every distinct Authorization value creates a SEPARATE cache entry,
  even for identical, cacheable, non-personalized content
1
A header intended for authentication is now also part of what determines a cache hit vs miss.
3
If the underlying response never actually varies by that header for public/cacheable content, this needlessly fragments the cache into near-duplicate entries per unique header value.

Why this works: A cache policy's job is to define exactly what should vary a cached response — including something that does not actually change the response silently destroys cache efficiency, since CloudFront has no way to know the header was irrelevant to the content.

Forwarding every header/cookie/query string "just in case," widening the cache key unnecessarily

Wrong

text
# Cache policy: include ALL headers, ALL cookies, ALL query strings
# "so nothing ever gets cached incorrectly"

Better

text
# Include only the specific headers/cookies/query strings that actually
# change the response — narrow the cache key deliberately

What you see: Cache hit rate stays surprisingly low despite serving largely identical content, and CloudWatch cache-hit metrics show most requests going all the way to the origin.

Why: Every additional dimension in the cache key (a header, a cookie, a query parameter) multiplies the number of distinct cache entries for what may be identical underlying content — the safe-sounding "include everything" default is exactly what silently defeats caching.

A request through CloudFront
routed toon a miss,matched byfetches from

Viewer request

Nearest edge location

cache hit → served immediately

Cache behavior match

by path pattern

Origin (S3, ALB, custom)

fetched only on a cache miss

  • Viewer request
    • leads to Nearest edge location (routed to)
  • Nearest edge location — cache hit → served immediately
    • leads to Cache behavior match (on a miss, matched by)
  • Cache behavior match — by path pattern
    • leads to Origin (S3, ALB, custom) (fetches from)
  • Origin (S3, ALB, custom) — fetched only on a cache miss

Remember: Distribution ties origins + cache behaviors together. Cache policy = what varies the cache key. Origin request policy = what gets forwarded to the origin on a miss. Keep the cache key as narrow as the content actually requires.

See also: s3 and alb origin patterns · cache keys ttl and invalidation

CloudFront + S3 vs CloudFront + ALB Patterns

coreintermediate

CloudFront + S3 fits static content (images, JS/CSS bundles, downloads) — Origin Access Control keeps the bucket private, reachable only through CloudFront. CloudFront + ALB/API Gateway fits dynamic content where responses vary per request — CloudFront still helps with TLS termination, edge caching of cacheable responses, and DDoS absorption, even when most responses aren't cached at all.

Think of it as

S3 as an origin is a warehouse of finished goods CloudFront can freely photocopy and hand out. An ALB/API origin is a kitchen that cooks each order fresh — CloudFront still stands at the front door checking IDs and absorbing the crowd, but most orders still have to reach the kitchen.

text
S3 origin: bucket kept private, OAC grants CloudFront-only read access
ALB/API origin: TLS termination + DDoS absorption apply, even with a low cache hit rate

What we're doing: See one distribution route static and dynamic paths to two different origins.

mixed-origins.txttext
Cache behavior "/static/*" → origin: my-assets-bucket (S3, via OAC)
Cache behavior "/api/*"    → origin: app-alb.us-east-1.elb.amazonaws.com (mostly no-cache)
1
Static assets are served from S3 through CloudFront's cache, with the bucket itself kept private via Origin Access Control.
2
API paths route to the ALB origin instead — CloudFront still terminates TLS and sits in front of the ALB, even though most responses here are not cacheable.

Why this works: A single distribution deliberately supporting multiple origins with different cache behaviors is what lets one app serve both static assets and a dynamic API through the same edge network, each getting the caching treatment appropriate to it.

Making an S3 origin bucket directly public instead of using Origin Access Control

Wrong

text
# Bucket policy grants public read directly, "so CloudFront can fetch objects"

Better

text
# Use Origin Access Control (OAC) — CloudFront can read the bucket while
# it stays otherwise private, with no direct public access at all

What you see: Objects remain directly fetchable by bypassing CloudFront entirely (going straight to the S3 URL), defeating any access controls, signed URLs, or caching CloudFront was supposed to enforce.

Why: A directly public bucket can be reached with or without going through CloudFront — Origin Access Control exists precisely so CloudFront can be the only path to the content, letting the bucket itself stay private and any signed-URL/cookie restrictions actually be enforced.

One distribution, two origins by path

/static/* → S3 (via OAC)

  • +A warehouse of finished goods
  • +Bucket stays private, reachable only via CloudFront
  • +Fully cacheable at the edge

/api/* → ALB

  • A kitchen cooking each order fresh
  • TLS termination + DDoS absorption still apply
  • Mostly uncacheable, and that's fine
  • /static/* → S3 (via OAC)
    • A warehouse of finished goods
    • Bucket stays private, reachable only via CloudFront
    • Fully cacheable at the edge
  • /api/* → ALB
    • A kitchen cooking each order fresh
    • TLS termination + DDoS absorption still apply
    • Mostly uncacheable, and that's fine

Choosing the origin pattern

Choosing the origin pattern
Content typePattern
Static assets (images, JS/CSS, downloads)CloudFront + S3, with Origin Access Control
Dynamic API responses, mostly uncacheableCloudFront + ALB/API Gateway
A single app serving bothOne distribution, path-based cache behaviors routing to each origin

Together

text
Cache behavior "/static/*" → S3 origin
Cache behavior "/api/*" → ALB origin (mostly no-cache)

Remember: CloudFront + S3 (via Origin Access Control) fits static content, kept otherwise private. CloudFront + ALB/API Gateway fits dynamic content — TLS termination and DDoS absorption still apply even with a low cache hit rate. One distribution can mix both via path-based cache behaviors.

See also: cloudfront core vocabulary · alb vs nlb

Cache Keys, TTL, and Invalidation

standardintermediate

TTL controls how long an edge location keeps a cached response before checking the origin again — 24 hours by default if the origin sends no caching headers. Invalidation forces a manual cache clear but costs money and does not reach caches CloudFront does not control (browser, corporate proxy). AWS's own recommendation for frequently-updated content is versioned filenames instead of invalidation.

Think of it as

TTL is a self-expiring "best by" date every edge copy carries. Invalidation is manually recalling every copy you can find — but you can only recall what is inside your own warehouse (CloudFront), not copies a customer already took home (a browser or proxy cache).

bash
aws cloudfront create-invalidation --distribution-id ABCDEF --paths "/images/*"

What we're doing: See why a "fixed" file can still appear broken to some users right after an invalidation.

invalidation-gap.txttext
aws cloudfront create-invalidation --distribution-id ABC --paths "/app.js"
→ CloudFront edge caches are cleared for /app.js
→ A user's own browser cache (or a corporate proxy) may still hold the
  OLD /app.js until its own separate TTL expires — invalidation never touched it
1
The invalidation request only reaches CloudFront's own edge cache layer.
3
Any cache CloudFront does not control — the browser's cache, an ISP or corporate proxy — was never part of the invalidation request and keeps serving its own copy until it independently expires.

Why this works: This gap is exactly why AWS recommends versioned filenames for content that changes often — a new filename is guaranteed to be a cache miss everywhere, including caches invalidation can never reach, while an invalidated old filename can still linger in places outside CloudFront's control.

Relying on invalidation as the primary deployment strategy for frequently-changing assets

Wrong

bash
# Every deploy: aws cloudfront create-invalidation --paths "/*"

Better

text
# Give each build a unique filename (content hash) and reference the new
# name — old cached copies simply age out, no invalidation needed at all

What you see: Invalidation costs accumulate every deploy, and some users still intermittently see stale content anyway, because invalidation never reached their local browser or proxy cache.

Why: Invalidating on every deploy pays a recurring cost for a guarantee it cannot actually fully deliver — versioned filenames solve the same problem for free and more completely, since a new URL is a cache miss by construction in every cache layer, not just CloudFront's.

Remember: TTL defaults to 24 hours with no origin caching headers. Invalidation costs money past a free allotment and never reaches caches outside CloudFront (browser, proxy). Prefer versioned filenames over invalidation for frequently-updated content — AWS's own recommendation.

See also: cloudfront core vocabulary · s3 and alb origin patterns

Advertisement