CloudFront, Deconstructed

CloudFront, Deconstructed

An advanced, interview-focused walkthrough of how CloudFront actually caches, routes, and executes at the edge — the four-event request lifecycle, cache key composition, origin shield, Lambda@Edge versus CloudFront Functions, and the failure modes that only surface once you're running real production traffic through it, not just serving a static bucket.

Most engineers meet CloudFront as “the thing that puts a CDN in front of an S3 bucket.” That description is accurate and almost useless for the person who has to actually run production traffic through it. This article assumes you already know what a distribution, an origin, and a cache behavior are. We won’t re-explain them. Instead, we go under the hood: how a request actually gets routed to a specific edge location, what really composes a cache key beyond the URL path, how the four-event Lambda@Edge/CloudFront Functions model lets you intercept a request at four genuinely different points with four genuinely different trade-offs, how origin shield changes your origin’s load profile, and how real companies have used CloudFront for things far more sophisticated than serving images fast.

1Advanced Core Concepts

Skipping the basics on purpose — this is the mental model of CloudFront as a programmable, hierarchical cache, not a static content shortcut.

CloudFront Is a Hierarchy, Not a Flat Mesh of Edge Servers

The advanced mental model correction most engineers need: CloudFront isn’t hundreds of independent edge caches all directly hitting your origin on a miss. Requests land at an edge location (a Point of Presence, or POP) nearest the viewer, and on a cache miss, that edge location doesn’t necessarily go straight to your origin — it typically consults a Regional Edge Cache first, a larger, longer-retention caching tier positioned between edge POPs and your origin, and only travels to your actual origin if the content isn’t cached there either. This hierarchy exists specifically to reduce origin load: content that’s popular in one metro but requested from multiple nearby edge POPs can be served from the shared regional tier without each individual edge location independently generating an origin request.

Analogy

Think of a national retail chain’s supply structure. Corner stores (edge POPs) don’t each place a separate order directly to the factory (your origin) every time they run low on a popular item. They restock from a regional distribution warehouse (the regional edge cache) that itself orders from the factory in bulk. The factory only gets hit directly when something is genuinely novel or rare enough that even the regional warehouse doesn’t have it in stock.

Origin Shield: An Explicit, Additional Caching Tier You Opt Into

Beyond the automatic edge-and-regional hierarchy, Origin Shield is an additional, single caching layer you explicitly designate at a specific AWS Region — ideally the Region closest to your origin — that all regional edge caches must go through before reaching the origin. This collapses what could be dozens of simultaneous regional-edge-cache-to-origin requests during a cache-miss stampede into effectively one, from Origin Shield’s perspective, dramatically reducing origin load during traffic spikes or after a mass cache invalidation. The advanced trade-off: Origin Shield adds one more network hop and a small amount of latency to true cache-miss requests, in exchange for materially better origin protection — a trade worth making for almost any origin that isn’t itself infinitely scalable (which is most origins that aren’t S3).

Cache Behaviors Are a Routing Table, Not Just a TTL Setting

A cache behavior is frequently treated as “the place where I set a TTL,” but it’s really a full routing and policy rule matched against a path pattern — determining which origin serves a request, which cache policy and origin request policy apply, which viewer protocol is required, which functions execute, and whether the response is compressed. Advanced distributions commonly define behaviors for wildly different purposes on a single distribution: one behavior for static assets pointed at S3 with aggressive caching, another for an API path pointed at an Application Load Balancer with caching effectively disabled, and another for a personalization path invoking a CloudFront Function to rewrite the cache key. Treating a distribution as a single monolithic cache configuration, rather than a set of independently-tuned routing rules, is where most advanced designs actually differ from beginner ones.

Cache Policy

What Composes the Cache Key

Determines which headers, query strings, and cookies are included in the cache key — the single most consequential setting for both cache hit ratio and correctness of personalized responses.

Origin Request Policy

What Reaches the Origin

Independently controls which headers/cookies/query strings are forwarded to the origin on a cache miss — deliberately separate from the cache key, so you can forward more to the origin than you cache on.

Response Headers Policy

What Reaches the Viewer

Injects or overrides response headers (CORS, security headers) without touching origin logic — commonly used to add HSTS or CSP headers uniformly across every origin behind the distribution.

Origin Groups

Primary/Failover Routing

A named pair of origins where CloudFront automatically retries a failed primary-origin response against a designated secondary — a native failover mechanism independent of any DNS-based approach.

i
What an interviewer may ask

“Your cache hit ratio dropped sharply after adding a personalization query parameter. Why, and what would you do?” A strong answer: including that query string in the cache key (via the cache policy) means every unique parameter value now produces a distinct cache entry, fragmenting what was previously one cacheable object into many near-duplicate ones. The fix isn’t necessarily to drop the parameter from caching entirely — it’s often to normalize or bucket it at the edge (via a CloudFront Function) before it reaches the cache-key computation, preserving personalization while limiting cache fragmentation.

Netflix’s Open Connect as the Extreme Version of the Same Idea

Netflix operates its own purpose-built CDN, Open Connect, rather than relying on CloudFront for video delivery at its scale — but the architectural principle is the same one CloudFront implements generally: place caching infrastructure as close to the ISP’s own network as possible, and build a hierarchy so that popular content is served from the tier closest to demand, minimizing the traffic that ever needs to traverse back to a centralized origin. Understanding CloudFront’s edge/regional-edge/shield hierarchy is, in effect, understanding a smaller-scale, generalized version of the same problem Netflix solved with dedicated hardware.

Price Classes: Trading Global Reach for Cost Control

CloudFront lets you restrict which edge locations a distribution actually uses via a price class setting — the full global set, or a subset excluding the more expensive regions (typically parts of South America, Australia, and select others). This is a deliberate, advanced cost-control lever distinct from performance tuning: choosing a restricted price class means viewers in excluded regions are still served, but from a farther-away edge location outside that region, trading their latency for a lower overall bill. The decision hinges entirely on where your actual audience is concentrated — restricting price class for a distribution serving a genuinely global audience with meaningful traffic from excluded regions actively degrades their experience for savings that may not be worth it, while the same restriction for a business with no meaningful presence in those regions is close to free cost reduction.

Multi-Origin Distributions as a Single Content Delivery Surface

A single distribution routinely fronts several logically distinct origins simultaneously — static assets from S3, an API from an Application Load Balancer, user-generated content from a different S3 bucket — unified under one domain and one certificate via path-pattern-scoped cache behaviors. This is an advanced simplification most beginner mental models miss: “the origin” is often not singular at all, and understanding a production distribution means reading its full set of behaviors as a routing table across multiple backends, not assuming one distribution equals one backend service.

2Internal Working

How a request actually reaches an edge location, and what happens at each of the four points CloudFront lets you intercept it.
flowchart TB
    V["Viewer"] --> DNS["Anycast DNS Routing
nearest edge POP"] DNS --> EDGE["Edge POP
Cache Check"] EDGE -->|"cache hit"| V EDGE -->|"cache miss"| REC["Regional Edge Cache"] REC -->|"cache hit"| EDGE REC -->|"cache miss"| SHIELD["Origin Shield
(optional, single region)"] SHIELD -->|"cache hit"| REC SHIELD -->|"cache miss"| ORIGIN["Origin
S3 / ALB / Custom HTTP"] ORIGIN --> SHIELD SHIELD --> REC REC --> EDGE EDGE --> V
Fig 1 — The caching hierarchy a single cache-miss request traverses before hitting the actual origin

Anycast Routing: Why “Nearest” Isn’t Always Geographically Nearest

CloudFront uses Anycast IP addressing, meaning the same IP address is announced from many edge locations simultaneously, and standard internet BGP routing — not a CloudFront-controlled decision — determines which physical edge location a given viewer’s request actually reaches. This is why “nearest edge location” is really “the edge location that current internet routing considers topologically closest,” which can occasionally diverge from straight-line geographic distance based on ISP peering arrangements. It’s a detail worth knowing precisely because it explains occasional, hard-to-reproduce reports of unexpectedly high latency from a specific ISP or region — the fix, when it’s a fix at all, often involves working with AWS support on routing rather than anything configurable in the distribution itself.

The Four-Event Model: Viewer Request, Origin Request, Origin Response, Viewer Response

CloudFront exposes exactly four points in a request’s lifecycle where custom code — CloudFront Functions or Lambda@Edge — can run, and the advanced skill is knowing which event fits which job, because using the wrong one either doesn’t work or costs far more than necessary. Viewer Request fires before the cache is even checked, ideal for cheap, high-volume logic like URL rewrites, header inspection, or redirect logic based on device type. Origin Request fires only on a cache miss, right before the request reaches the origin — the right place for logic that should only run when actually hitting the origin, like adding an authentication header the origin needs. Origin Response fires after the origin replies, before the response is cached — useful for modifying or enriching the origin’s response (adding a security header, normalizing an error page) before it’s stored and served to future viewers. Viewer Response fires just before the response returns to the viewer, on every request regardless of cache status — appropriate for logic that must apply universally, like injecting a nonce for CSP headers.

sequenceDiagram
    participant Viewer
    participant Edge as Edge Location
    participant Origin

    Viewer->>Edge: Request
    Note over Edge: Event 1 — Viewer Request
(CloudFront Functions or Lambda@Edge) Edge->>Edge: Check cache alt Cache Miss Note over Edge: Event 2 — Origin Request
(Lambda@Edge only) Edge->>Origin: Forward request Origin-->>Edge: Response Note over Edge: Event 3 — Origin Response
(Lambda@Edge only) Edge->>Edge: Store in cache end Note over Edge: Event 4 — Viewer Response
(CloudFront Functions or Lambda@Edge) Edge-->>Viewer: Response
Fig 2 — Four interception points, but Origin Request and Origin Response only fire on a genuine cache miss

CloudFront Functions Versus Lambda@Edge: Not Interchangeable

These are frequently presented as “lightweight versus heavyweight” versions of the same thing, but the advanced distinction runs deeper. CloudFront Functions run in a JavaScript-only, sub-millisecond execution environment directly on the edge location itself, support only the Viewer Request and Viewer Response events, cannot make network calls, and are priced for extremely high request volumes at very low cost. Lambda@Edge runs full Node.js or Python Lambda functions, supports all four events (uniquely including Origin Request and Origin Response), can make network calls and access more compute and memory, but executes at a smaller subset of locations (Regional Edge Caches for Origin Request/Response events, not every edge POP) and carries meaningfully higher latency and cost per invocation. The decision isn’t “which is better” — it’s “does this logic need origin-facing events or network calls,” which only Lambda@Edge provides, or is it viewer-facing, high-volume, and simple enough for CloudFront Functions’ constraints.

Cold Starts and Execution Location Matter for Lambda@Edge Specifically

Because Lambda@Edge functions for Viewer Request/Response events execute at edge locations while Origin Request/Response events execute at the more limited set of Regional Edge Caches, a function’s actual execution latency and cold-start behavior can differ meaningfully depending on which event it’s attached to — a detail that surprises teams who assume “Lambda@Edge” is a single uniform execution environment regardless of which of the four events triggered it. Advanced performance testing for a Lambda@Edge-heavy distribution measures latency per event type specifically, rather than trusting a single aggregate number that blends genuinely different execution characteristics together.

Header, Cookie, and Query String Forwarding Are Independently Configurable at Every Layer

It’s a common intermediate-level assumption that “what’s cached” and “what’s sent to the origin” are the same set of parameters — they’re deliberately not. The cache policy governs the cache key; the origin request policy governs what’s forwarded to the origin on a miss, and these can differ intentionally. A common advanced pattern forwards an authentication header to the origin (via the origin request policy) without including it in the cache key (via the cache policy) — because the response doesn’t actually vary based on that header’s value, but the origin still needs it to authorize the request. Conflating the two, or assuming CloudFront’s default forward-everything behavior is safe, is a frequent source of both cache fragmentation and accidental information leakage into cached responses.

3Data Flow & Lifecycle

Cache key composition, TTL resolution order, and what actually happens during an invalidation.

Cache Key Composition Is a Deliberate, Independent Decision From TTL

A cache key is not simply “the URL.” It’s composed from whatever combination of URL path, query string parameters, headers, and cookies the attached cache policy specifies — and every additional component you include fragments your cache into more, smaller-hit-rate entries. The advanced discipline is including only what’s strictly necessary to differentiate genuinely distinct responses: if a page renders identically regardless of the `utm_source` query parameter, including it in the cache key needlessly multiplies cache entries for content that’s actually the same, cratering hit ratio for no correctness benefit.

TTL Resolution Order: Origin Headers Versus Policy Overrides

When both an origin’s `Cache-Control` or `Expires` header and a CloudFront cache policy’s min/max/default TTL settings are present, CloudFront resolves them with a specific precedence: if the origin’s header value falls within the policy’s configured min-to-max range, the origin’s value wins; if it falls outside that range, it’s clamped to the nearest boundary; if the origin sends no caching header at all, the policy’s default TTL applies. This means a cache policy functions as guardrails around origin-supplied freshness, not a hard override — an origin team that thinks they’ve set a five-minute TTL via their own headers can be silently overridden if a CloudFront cache policy’s minimum TTL happens to be set higher, a mismatch that causes real, hard-to-diagnose staleness complaints when the two teams aren’t coordinating.

Invalidation Is Not Instant, and Not Free at Scale

Requesting an invalidation doesn’t retroactively “un-cache” content instantaneously across every edge location worldwide the moment the API call returns — it propagates, typically completing within minutes, but not with a hard real-time guarantee, and in-flight requests already being served from a not-yet-invalidated edge location can still receive stale content during the propagation window. At scale, invalidations also have a cost model: the first 1,000 paths invalidated per month are free, and further invalidations, particularly wildcard invalidations that match many objects, are billed and rate-limited. Advanced designs avoid using invalidation as the primary mechanism for routine content updates — a versioned URL or cache-busting query string that produces a naturally distinct cache key on each deploy sidesteps invalidation entirely for the common case, reserving actual invalidation calls for genuine emergencies (a bad deploy that must be pulled immediately).

!
Failure Scenario

A team relies on wildcard invalidation (`/*`) after every deployment as their standard cache-refresh mechanism. Under moderate release frequency this quietly becomes both a meaningful line item on the AWS bill and a recurring multi-minute window of inconsistent responses across edge locations worldwide — a problem that a simple versioned-asset-path convention would have avoided from the start.

Negative Caching: TTLs for Error Responses

It’s easy to focus entirely on caching successful responses and overlook that CloudFront also supports configuring separate cache TTLs for specific error status codes returned by the origin — a deliberate, advanced protection against “error amplification,” where an origin already struggling under load (returning 5xx errors) gets hit even harder because every retry from every viewer generates a fresh, uncached request. Setting even a short TTL (a few seconds) on 5xx responses means CloudFront briefly serves the cached error to additional viewers rather than forwarding every single one to an already-struggling origin, giving it breathing room to recover rather than compounding the outage.

Range Requests and Byte-Range Caching for Large Objects

For large files — video, large downloadable archives — clients frequently request specific byte ranges rather than the entire object at once (video players seeking to a specific timestamp, for instance). CloudFront caches byte-range responses intelligently, avoiding the need to fetch and cache the entire multi-gigabyte object just to serve a small requested range, but this behavior interacts with cache key and origin configuration in ways that matter for large-media-heavy applications: an origin that doesn’t correctly support range requests (returning the full object regardless of the `Range` header) forces CloudFront into a much less efficient fallback pattern, and advanced media delivery architectures verify range-request support at the origin explicitly rather than assuming it works correctly by default.

4Advantages, Disadvantages & Trade-offs

At the advanced level, the honest framing isn’t “CloudFront makes things faster” — it’s “CloudFront trades a fresh, always-current view of your origin for latency reduction and origin-load protection, and every design decision from here is about how tightly you’re willing to bound that trade.”

↓ Latency
FOR CACHEABLE CONTENT
↓ Origin Load
VIA HIERARCHICAL CACHING
Δ Freshness
BOUNDED BY TTL, NOT REAL-TIME
Analogy

CloudFront is like a news wire service that distributes stories to local newspapers. A story published once can reach thousands of readers without the wire service re-fetching it for every single newspaper — a massive efficiency gain. But if the story gets corrected at the source, every already-printed local copy is stale until the correction propagates and the presses re-run. The efficiency and the staleness risk are the same mechanism, not two separate trade-offs.

The Trade-off Interviewers Actually Care About

The most tested trade-off is cache hit ratio versus content freshness versus personalization fidelity — the three pull in different directions simultaneously. Maximizing hit ratio wants a broad, simple cache key and long TTLs. Maximizing freshness wants short TTLs and frequent invalidation. Maximizing personalization wants the cache key to reflect user-specific state, which fragments the cache. Advanced architecture rarely optimizes any one of the three in isolation — it segments content by behavior (static assets get long TTLs and simple keys, personalized API responses get short or no caching, semi-dynamic content gets a moderate TTL with stale-while-revalidate-style patterns) rather than applying one global policy to an entire distribution.

Operational Simplicity Versus Edge Programmability

A distribution using only default caching behavior with no edge functions at all is trivially simple to reason about — what you see in the origin’s response is, modulo TTL, what the viewer eventually receives. The moment you introduce CloudFront Functions or Lambda@Edge, the distribution becomes a distributed, edge-executed piece of your application logic, with its own deployment process, its own debugging challenges (issues that only reproduce at specific edge locations), and its own latency and cost model. This isn’t a reason to avoid edge functions — many of the patterns in this article depend on them — but it is a genuine complexity cost that should be paid for deliberately, not accumulated function by function without anyone stepping back to ask whether the aggregate edge-logic footprint has become a second application nobody’s tracking as carefully as the origin’s own codebase.

5Performance & Scalability

Compression as a Free, Frequently Skipped Lever

CloudFront can automatically compress eligible content (text-based formats — HTML, CSS, JS, JSON, SVG) using gzip or Brotli when the viewer’s `Accept-Encoding` header indicates support, at no additional cost and with no origin-side change required beyond serving uncompressed content and letting CloudFront handle compression. This is a genuinely free performance win that a surprising number of production distributions leave disabled, either from unfamiliarity or because it was never revisited after initial setup — advanced audits of an existing distribution routinely find this as one of the highest-value, lowest-effort fixes available.

HTTP/2, HTTP/3, and Connection Reuse Economics

CloudFront’s support for HTTP/2 and HTTP/3 (QUIC) at the viewer connection matters most for pages with many small assets, because both protocols allow multiplexed requests over a single connection, avoiding the historical HTTP/1.1 pattern of opening many parallel connections (each with its own TLS handshake overhead) to fetch resources concurrently. The advanced nuance: this benefit is realized at the viewer-to-edge hop; the edge-to-origin hop’s connection behavior is governed separately by origin keep-alive settings, and a slow, connection-churning origin can still bottleneck cache-miss latency regardless of how efficient the viewer-facing protocol is.

LeverEffectCommon Oversight
Compression (gzip/Brotli)Reduces transferred bytes for text-based content with zero origin changeLeft disabled after initial distribution setup
Origin ShieldCollapses concurrent regional-edge-cache misses into far fewer origin requestsPlaced in a Region far from the actual origin, adding latency without the load-reduction benefit
Cache key minimizationHigher hit ratio by avoiding unnecessary key fragmentationQuery strings or headers included in the key “just in case,” without verifying they actually vary the response
Origin keep-alive tuningReduces repeated TLS handshake overhead on cache-miss traffic to the originLeft at framework defaults not tuned for CloudFront’s connection reuse pattern

Scaling Is Not the Bottleneck — Your Origin Usually Is

CloudFront itself scales to handle enormous request volume without customer-visible capacity planning — the practical scalability ceiling in most real deployments is the origin’s ability to handle cache-miss traffic, especially during a cold cache (post-deployment, post-invalidation, or for a newly launched distribution with no warm cache yet). Advanced launches for high-traffic events pre-warm the cache deliberately — issuing synthetic requests for known-popular paths ahead of the actual traffic spike — specifically to avoid the first wave of real users all generating simultaneous origin misses.

Regional Distribution of Cache-Miss Traffic Isn’t Uniform

Because viewer traffic reaches the nearest edge location, but cache misses funnel through regional edge caches and Origin Shield toward a single origin location, the geographic distribution of your viewer traffic doesn’t map linearly onto the origin’s request pattern — a globally distributed audience produces cache-miss traffic that’s already been substantially aggregated and smoothed by the caching hierarchy before it reaches the origin. This matters for origin capacity planning specifically: sizing an origin based on total global viewer request volume, rather than the actual, much smaller post-hierarchy cache-miss volume, leads to significant over-provisioning of origin capacity that a correctly-modeled hit-ratio assumption would have avoided.

Latency Percentiles, Not Just Averages, for Cache-Miss Paths

An average cache-miss latency figure can look perfectly acceptable while a meaningful tail of requests — often the ones involving a specific slow origin endpoint, a specific geographic path with unusual routing, or a cold Lambda@Edge execution — experience latency several times worse. Advanced performance work on CloudFront-fronted applications always examines p95 and p99 latency specifically for cache-miss requests, since that’s precisely the population most affected by any origin-side or edge-function-side inefficiency, and it’s the population an averages-only dashboard is most likely to hide.

6High Availability & Reliability

Origin Failover Groups Are Faster Than DNS-Based Failover

An origin group pairs a primary and secondary origin behind a single behavior, and CloudFront automatically retries against the secondary when the primary returns a configured set of failure status codes (5xx errors, or specific codes you designate) — this failover happens at the CDN layer itself, on a per-request basis, without any DNS propagation delay, making it materially faster than a Route 53 health-check-based failover for the specific case of an origin actively returning error responses (as opposed to being completely unreachable at the network level, which origin groups don’t cover as directly).

Stale-While-Error Behavior: Serving Cached Content During an Origin Outage

Beyond origin groups, CloudFront cache behaviors can be configured to serve stale cached content when the origin is unreachable or erroring, for a configurable grace period beyond the object’s normal TTL — a resilience pattern that keeps a site serving something (slightly outdated, but functional) rather than propagating a 5xx error to every viewer the moment an origin has a bad few minutes. This is a distinct mechanism from origin groups and can be used together with them: origin groups handle “try a different origin,” stale-while-error handles “if nothing works, serve what we already had.”

i
What an interviewer may ask

“Your origin had a 10-minute outage. What determined whether users noticed?” A strong answer separates two independent mechanisms: whether an origin group’s failover to a healthy secondary origin was configured (masking the outage entirely for eligible requests), and whether stale-while-error was enabled for cacheable content (serving slightly outdated but functional responses instead of hard failures for anything already cached). Neither happens automatically without explicit configuration — a distribution with neither will propagate the full outage to every viewer immediately.

Multi-Region Origin Design Beneath a Single Distribution

A single CloudFront distribution can front origins in entirely different AWS Regions (or entirely outside AWS), which means Region-level origin redundancy is a distribution configuration choice, not something requiring multiple distributions or complex DNS orchestration. Advanced designs for genuinely mission-critical content pair an origin group spanning two Regions with health-check-based automatic failover, so a full-Region origin outage is absorbed at the CDN layer rather than requiring a manual DNS cutover during the incident.

7Security

Origin Access Control: Closing the Direct-Origin-Access Gap

A distribution fronting an S3 bucket provides no security benefit at all if the bucket remains independently, publicly accessible — anyone can bypass CloudFront entirely and hit S3 directly, skipping every cache policy, security header, and edge function you’ve configured. Origin Access Control (the modern replacement for the older Origin Access Identity) restricts the S3 bucket to only accept requests signed as coming from your specific CloudFront distribution, closing that bypass path. Advanced security reviews of an existing CloudFront deployment specifically check for this — a surprisingly common finding is a distribution correctly configured with WAF and security headers, sitting in front of a bucket that’s still fully bypassable by anyone who knows or guesses its direct S3 URL.

Signed URLs and Signed Cookies: Two Different Access-Control Shapes

Both mechanisms restrict access to content using a cryptographic signature, but they fit different use cases. Signed URLs embed the signature and expiration directly in each individual URL — appropriate for granting access to a small number of specific files (a single premium video, a private document link shared once). Signed cookies grant access to an entire path pattern via a cookie set once at login, without needing to individually sign every asset URL — appropriate for a session where a user needs access to many resources under one prefix (an entire private video library) without rewriting every link in the application to carry a signature.

Field-Level Encryption for Defense-in-Depth on Sensitive Fields

For specific sensitive fields (a credit card number, a national ID number) within a larger request, field-level encryption lets CloudFront encrypt just those fields at the edge using a public key, such that only a specific downstream service holding the corresponding private key can decrypt them — meaning even if an intermediate component in your own infrastructure between the edge and that specific service is compromised or logs more than it should, the sensitive field itself remains encrypted throughout, not just protected by TLS in transit (which protects the whole payload only until it’s decrypted at the first hop that terminates TLS).

Rate Limiting and Bot Mitigation at the Edge

Beyond signature-based WAF rules, AWS WAF’s rate-based rules and managed bot-control rule groups can be attached to a CloudFront distribution to throttle or challenge traffic exhibiting scraping or credential-stuffing patterns before it ever reaches the origin. The advanced consideration here is tuning these rules against the distribution’s actual legitimate traffic shape — an aggressive rate limit set without accounting for legitimate high-volume API consumers (a mobile app’s background sync behavior, for instance) can inadvertently throttle real users, which is why rate-based rules are typically rolled out first in count-only mode to observe their effect before switching to actively blocking traffic.

ADR-030 · WAF Placement Decision Recorded
Context

Malicious traffic (SQL injection attempts, bot scraping, credential-stuffing) reaching the origin wastes origin capacity and risks exploitation even when the payload would ultimately be rejected by application-layer validation.

Decision

Attach AWS WAF directly to the CloudFront distribution rather than only at the origin (e.g., an ALB), so malicious requests are evaluated and blocked at the edge, before consuming any origin-facing capacity or bandwidth.

Consequence

Blocked requests never reach the origin, reducing both attack surface and unnecessary origin load, at the cost of needing WAF rule tuning to avoid false positives blocking legitimate edge-cached traffic patterns.

TLS Termination and Minimum Protocol Version Enforcement

CloudFront terminates TLS at the edge for the viewer connection, and the minimum TLS protocol version and cipher suite set accepted from viewers is independently configurable per distribution via a security policy setting — a control worth revisiting periodically rather than leaving at whatever default applied when the distribution was first created, since accepting outdated TLS versions for compatibility with legacy clients is a real, quantifiable security trade-off that should be a conscious decision, not an inherited default nobody revisited.

Geographic Restrictions as a Coarse but Useful Control

Geo-restriction lets a distribution allow or deny requests based on the viewer’s country, determined via IP geolocation — a coarse control (VPNs and proxies can circumvent it, and it operates at country granularity, not finer) but a genuinely useful first line of defense for compliance requirements (restricting content to specific licensed territories) or reducing exposure to traffic from regions with no legitimate user base for a given application, filtered at the edge before any origin-facing resources are consumed.

8Monitoring, Logging & Metrics

Cache Hit Ratio Is the Headline Metric, But Not the Only One That Matters

Cache hit ratio is the obvious first metric to watch, but advanced observability tracks it alongside origin latency specifically for cache-miss requests (since a low hit ratio combined with slow origin responses compounds into real user-facing latency far worse than either problem alone), and 4xx/5xx error rates split by whether they originated at the edge (a malformed request, a WAF block) or were passed through from the origin — conflating the two obscures whether a spike in errors is a CloudFront-layer or origin-layer problem.

Real-Time Logs Versus Standard Access Logs

Standard CloudFront access logs are delivered to S3 with a delay of typically minutes to hours, batched — perfectly adequate for retrospective analysis and long-term trend dashboards, but useless for detecting an active incident quickly. Real-time logs stream a configurable sample of request data to Kinesis Data Streams within seconds, at additional cost, and are the correct choice when the goal is near-live operational dashboards or triggering automated responses to an emerging pattern (a sudden spike in a specific error code, for instance) rather than after-the-fact analysis.

“A high cache hit ratio with a rising origin-miss latency trend is a warning sign hiding behind a metric that still looks healthy in aggregate.”

Correlating Edge Function Errors With Cache Behavior

A bug in a CloudFront Function or Lambda@Edge function can silently degrade an entire distribution’s behavior — a Viewer Request function that throws on certain inputs can cause CloudFront to return a generic error for those requests, and because the function itself runs across many independent edge locations, the failure can appear geographically clustered or inconsistent in a way that’s genuinely confusing without CloudWatch metrics and logs specifically scoped to that function’s execution, separate from general distribution-level metrics.

Alarming on Origin-Attributed Versus Edge-Attributed Errors Separately

A single aggregate error-rate alarm on a distribution conflates two very different incidents: the origin returning genuine application-level errors, and CloudFront itself rejecting requests at the edge (a WAF block, a malformed request, a certificate issue). Advanced alerting configurations split these explicitly, because the correct response team and remediation differ entirely — an origin-attributed error spike needs the application team, while an edge-attributed spike needs whoever owns the WAF rules or distribution configuration, and paging the wrong team based on a conflated metric wastes precious incident-response time during an active outage.

9Deployment & Cloud

Continuous Deployment for Distributions: Staged Configuration Rollouts

CloudFront supports continuous deployment policies that let you roll out a distribution configuration change to a percentage of traffic first — testing a new cache policy, origin, or function change against real production traffic at a small scale before promoting it to 100% — directly analogous to a canary deployment pattern, but implemented at the CDN configuration layer itself rather than requiring a separate feature-flagging system or duplicate distributions.

Infrastructure as Code for Distributions

Distributions, cache policies, origin request policies, and response headers policies are all first-class resources in Terraform and CloudFormation, and advanced practice treats every one of these as versioned, reviewed configuration rather than console edits — particularly because cache policy changes can have wide, immediate blast radius (a mis-scoped cache key change affecting hit ratio for every path matching that behavior) and benefit enormously from the same review discipline applied to application code changes.

Multi-Tenant Distributions Versus One Distribution Per Application

A single distribution can front many different origins across many path patterns via cache behaviors, and organizations face a real architectural choice between consolidating many applications behind one shared distribution (simpler DNS and certificate management, shared WAF rules) versus giving each application its own distribution (cleaner blast-radius isolation — a misconfiguration or quota issue in one application’s distribution can’t affect another’s). Advanced platform teams building internal CDN-as-a-service offerings typically default to per-application distributions specifically for this isolation property, accepting the added management overhead of more distributions to manage.

Certificate Management at Scale

Custom domain names on a distribution require an ACM certificate provisioned in the us-east-1 Region specifically, regardless of which Region the distribution’s origins actually live in — a frequently surprising constraint for teams used to provisioning ACM certificates in whichever Region their other infrastructure resides. At scale, organizations managing many distributions across many custom domains benefit from a deliberate certificate rotation and renewal monitoring strategy, since a lapsed certificate on a high-traffic distribution is a full, immediate outage for every viewer using that custom domain, not a gradual degradation.

Cross-Account and Cross-Organization Distribution Ownership

In larger organizations, it’s common for a platform or infrastructure team to own the CloudFront distributions themselves while individual application teams own the origins behind specific cache behaviors — a separation of concerns that requires deliberate process for how an application team requests a new cache behavior, gets an origin added, or needs a cache policy adjusted, since they typically don’t have direct write access to the shared distribution’s configuration. Advanced organizations formalize this with a self-service request process or an internal API/pipeline specifically for onboarding a new origin behind an existing distribution, rather than relying on ad hoc tickets to the platform team for every change.

10Design Patterns & Anti-patterns

Pattern

Versioned Asset Paths

Content-hashed or version-numbered file paths (e.g., `/assets/app.a1b2c3.js`) make every deploy naturally produce new, distinct cache keys — sidestepping invalidation entirely for the common deployment case.

Pattern

Edge-Side Personalization via CloudFront Functions

Normalizing or bucketing personalization signals (device type, coarse geography) at the Viewer Request event before the cache key is computed, preserving most caching benefit while still enabling limited personalization.

Anti-pattern

Caching Everything, Including Truly Dynamic Responses

Applying a uniform, aggressive cache policy across an entire distribution regardless of content type, occasionally caching genuinely per-user API responses and serving one user’s data to another — a serious correctness and privacy bug, not just an inefficiency.

Anti-pattern

Direct Origin Exposure

Leaving the origin (an S3 bucket or ALB) independently, publicly reachable, letting attackers or scrapers bypass every CloudFront-layer protection — caching, WAF, security headers — entirely.

i
What an interviewer may ask

“How would you serve both a highly cacheable marketing site and a per-user dashboard from the same domain without leaking data between users?” A strong answer separates concerns by cache behavior: the marketing paths get a broad cache policy with long TTLs, while the dashboard’s API paths get a cache policy that either disables caching entirely or scopes the cache key to include the authenticated user’s session identifier — never relying on a single distribution-wide default policy to correctly handle both cases.

11Best Practices & Common Mistakes

PracticeWhy It’s Advanced, Not Basic
Restrict origins with Origin Access ControlPrevents attackers from bypassing every CloudFront-layer protection by hitting the origin directly
Minimize cache key components to only what genuinely varies the responseDirectly determines cache hit ratio; unnecessary inclusion silently fragments the cache
Use versioned asset paths instead of routine wildcard invalidationAvoids both invalidation cost at scale and the propagation-delay staleness window
Enable Origin Shield for any non-infinitely-scalable originCollapses concurrent regional-edge-cache misses into far fewer origin requests during spikes
Pick CloudFront Functions vs Lambda@Edge based on event needed, not just “lighter is better”Origin Request/Response logic and network calls are only possible with Lambda@Edge — the choice is functional, not just about cost
Split monitoring by cache-hit vs cache-miss latency and edge-origin error attributionAn aggregate metric can hide a real, worsening origin-layer problem behind a still-acceptable overall number
!
Common Mistake

Treating a CDN as a purely static-content tool and never revisiting the distribution’s configuration as the application evolves to include dynamic, personalized, or API-driven paths. A cache policy tuned for a marketing site quietly applied to a growing set of dynamic endpoints is a common, slow-burning source of both stale-data complaints and privacy near-misses.

Load Testing the Cache Layer Deliberately, Not Just the Origin

Most performance and load-testing exercises focus on the origin’s capacity, treating the CDN as a given that “just works.” Advanced testing practice specifically validates cache behavior under load — confirming that a cache-key configuration behaves as expected under realistic traffic diversity (not just a single synthetic test URL repeated), that Origin Shield actually collapses concurrent misses as designed during a simulated cold-cache stampede, and that edge functions don’t introduce unexpected latency or errors under the concurrency levels a real launch will produce, rather than only having been tested with a handful of manual requests during development.

Documenting the Cache Strategy as a Living Artifact

Because a distribution’s cache behaviors, policies, and edge functions accumulate over time, often across different engineers and different periods of the application’s growth, advanced teams maintain an explicit, current document describing the intended caching strategy per path pattern — what should be cached, for how long, and why — separate from the raw Terraform or console configuration itself. Without this, a distribution’s actual behavior becomes archaeology: the only way to know why a specific path has a five-minute TTL is to find whoever set it, if they’re still around, rather than reading a maintained rationale.

12Real-World & Industry Examples

Slack — Edge-Side Redirect and Device Logic

Consumer-facing web applications with heavy mobile-versus-desktop divergence use Viewer Request functions to route users to the correct experience at the edge, avoiding an origin round trip purely to determine which version of a page to serve.

Vevo — Signed URLs for Premium Video Access

Media platforms restricting premium video content use signed URLs with short expirations tied to an authenticated session, preventing link-sharing from granting indefinite unauthorized access to content that should only be playable within a valid, time-bound session.

Financial Services — Field-Level Encryption for Application Forms

Organizations collecting sensitive identifiers through web forms (loan applications, account openings) have used field-level encryption to ensure specific sensitive fields remain encrypted end-to-end to the specific backend service authorized to decrypt them, independent of how many intermediate services the broader request payload passes through.

E-Commerce Platforms — Origin Shield During Flash Sales

Retailers running high-traffic flash sales enable Origin Shield specifically ahead of the event, collapsing what would otherwise be a origin-overwhelming stampede of near-simultaneous cache misses (as caches expire and traffic surges together) into a bounded, manageable request volume the origin can actually sustain.

News Publishers — Negative Caching During Traffic Surges

Publishers experiencing sudden traffic surges from a breaking story configure short negative-caching TTLs on 5xx responses specifically so that if the origin does briefly buckle under load, the resulting error page is itself cached for a few seconds rather than every single concurrent viewer independently hammering the already-struggling origin, giving it a chance to recover rather than compounding the outage.

Across these examples, the pattern repeats: the teams getting the most value from CloudFront treat it as a programmable request-processing layer with genuine architectural leverage, not merely a passive cache sitting in front of an unchanged origin.

13FAQ

Q1Why did my cache hit ratio drop after adding a new query parameter to my URLs?
If the cache policy attached to that behavior includes query strings in the cache key (or is configured to include all of them), each unique parameter value generates a distinct cache entry, fragmenting what may have been one cacheable response into many — normalize or exclude parameters that don’t actually change the response before they reach the cache key.
Q2Does an S3 bucket behind CloudFront need to be public?
No, and it shouldn’t be. Origin Access Control lets the bucket remain fully private while trusting only signed requests from your specific CloudFront distribution, closing the direct-access bypass that a public bucket would otherwise leave open.
Q3When should I use Lambda@Edge instead of CloudFront Functions?
Whenever the logic needs to run on Origin Request or Origin Response events, needs to make an outbound network call, or needs more compute/memory or a runtime beyond CloudFront Functions’ constrained JavaScript environment — CloudFront Functions cover only Viewer Request/Response and cannot call out to other services.
Q4Is invalidation instant across every edge location?
No — it typically completes within minutes, not instantaneously, and in-flight requests already being served from a not-yet-invalidated edge location can briefly still receive stale content during that propagation window.
Q5What’s the difference between an origin group and stale-while-error behavior?
An origin group automatically retries a failed request against a designated secondary origin. Stale-while-error instead serves already-cached content past its normal TTL when the origin is unreachable or erroring. They solve different problems and are commonly used together for maximum resilience.
Q6Can one CloudFront distribution safely serve both a public marketing site and a private, per-user API?
Yes, but only with distinct cache behaviors per path pattern, each with its own cache policy appropriate to that content’s sharing characteristics — a single distribution-wide caching default applied uniformly to both is what causes accidental cross-user data leakage.
Q7Why does my ACM certificate for a CloudFront custom domain have to be in us-east-1?
CloudFront is a global service, and its certificate integration specifically requires ACM certificates provisioned in the us-east-1 Region regardless of where your origins actually live — a fixed platform requirement, not a configuration choice, and a common source of confusion for teams whose other infrastructure lives entirely in a different Region.
Q8Does enabling compression require any changes on my origin?
No — CloudFront can compress eligible text-based content automatically based on the viewer’s Accept-Encoding header, with the origin continuing to serve uncompressed content exactly as before; it’s a distribution-level setting, not an origin-side change, which is why it’s such a frequently overlooked, effectively free performance win.

14Summary and Key Takeaways

Carry These Forward

  • CloudFront is a hierarchy — edge POP, regional edge cache, optional Origin Shield — not a flat mesh, and that hierarchy is what protects your origin from cache-miss stampedes.
  • Cache key composition is the single highest-leverage lever for hit ratio — include only what genuinely varies the response, never “just in case.”
  • The four-event model gives four genuinely different tools — pick CloudFront Functions or Lambda@Edge based on which event and capability the logic actually needs, not by default habit.
  • Origin Access Control is mandatory, not optional — a CloudFront distribution in front of a publicly reachable origin provides no real security benefit.
  • Origin groups and stale-while-error solve different resilience problems and are frequently used together for genuine outage tolerance.
  • Invalidation is a blunt, propagation-delayed, cost-bearing tool — versioned asset paths solve the routine deployment case far more cleanly.
  • Segment cache policy by content behavior, not one global setting — static, personalized, and dynamic content need genuinely different caching strategies on the same distribution.