253: CDNs, Edge Caching, Static Assets, and Edge Compute
Learning outcomes
By the end of this lesson, you can:
- explain and apply points of presence in a realistic implementation;
- explain and apply cache keys in a realistic implementation;
- explain and apply ttl and revalidation in a realistic implementation;
- explain and apply invalidation in a realistic implementation;
- explain and apply origin shielding in a realistic implementation.
Prerequisites and retrieval
This lesson assumes that you have the 01–06 foundation and have worked through the preceding lessons in this module. Before you start, retrieve one concrete example from an earlier project where one of these concerns appeared. Perhaps you served a static bundle, cached an API response, dealt with stale content, or put a reverse proxy in front of an application. The point is not to recite vocabulary. It is to use the vocabulary to make a defensible decision in a large-scale distributed service.
For each decision, make the requirements, traffic shape, failure modes, cost, and operational constraints explicit. A CDN configuration that looks reasonable for public images may be unsafe for a personalized response, and a cache policy that works at low traffic may overload the origin when a popular object expires. Those differences are part of the design, not implementation details to postpone.
Terminology
- Points of presence: A CDN replicates or caches content at geographically distributed edge locations. Clients can therefore fetch content from a nearby network location instead of making every request travel to the origin region. A point of presence is not the origin datastore; it is a location in the delivery layer that may hold a cached representation.
- Cache keys: A cache key identifies which requests are allowed to share a cached response. The URL and query string may matter, as may selected headers, cookies, device context, or authorization context when those inputs change the representation. Omitting a representation-changing input can serve the wrong response, including one user's content to another user.
- TTL and revalidation: A time-to-live (TTL) controls how long a cached response is considered fresh. Revalidation lets a cache ask the origin whether its existing object is still current, commonly with
ETagorLast-Modified, instead of downloading the full representation again.stale-while-revalidateis another policy that can trade bounded staleness for lower latency and better origin protection. - Invalidation: Invalidation removes or marks cached content as unusable before its normal freshness period ends. Purging content across many edge locations can be slow and costly, so it should not be the only way a system guarantees that a newly published asset is reachable.
- Origin shielding: A shield layer sits between edge locations and the origin. It collapses misses from many edges into fewer requests and protects the origin when objects expire simultaneously or a popular object suddenly receives a burst of traffic.
- Edge compute: Workers or functions running at the edge can perform lightweight routing, authentication checks, or personalization close to the client. Stateful logic is harder there because data locality, cold starts, observability, vendor limits, and consistency all become part of the design.
Mental model
Treat CDNs, Edge Caching, Static Assets, and Edge Compute as a design problem with observable inputs, outputs, invariants, and failure modes. A CDN can reduce latency and origin load by serving cacheable content near users, but it does not make every response safe to cache. The cache key, invalidation strategy, authorization model, and degree of personalization determine whether a response can be shared.
A useful invariant is: requests that share a cache entry must be allowed to receive the same representation. If user identity, locale, device class, authorization, or another input changes the response, that input must be represented in the cache key or the response must bypass the shared cache. Freshness is a separate invariant: a response must not be presented as current beyond the policy the product can tolerate.
A strong implementation makes those assumptions visible. Put validation at boundaries, narrow uncertainty before it reaches the next layer, and leave enough evidence—tests, types, constraints, metrics, or diagrams—to explain why the design is safe. This is also what makes a production incident diagnosable: you can tell whether a wrong response came from the browser cache, an edge cache, a shield, or the origin.
A useful interview and production sequence is:
requirement -> constraints -> model -> implementation -> failure analysis -> verification
Do not jump from a requirement straight to a CDN feature or a library call. First state what must remain true. Then choose the mechanism that enforces it, and define how you will observe a hit, miss, stale response, purge, or origin failure.
Deep dive
1. Points of presence
When every client request travels to one origin region, distance and network conditions add latency even if the application itself is fast. CDNs replicate or cache content at geographically distributed edge locations, called points of presence, so clients often fetch from a nearby network rather than from the origin region.
This helps most when the response is cacheable and many users request the same representation. It does not remove the need to design the origin path: a cache miss, an uncached personalized request, or a purge still has to reach the origin. You should also account for the operational cost of another delivery layer and for the possibility that different edge locations have different cache state during propagation.
Decision rule: Use points of presence deliberately when they make the delivery contract or an invariant easier to prove. If they only reduce typing while hiding an assumption, prefer the more explicit design.
2. Cache keys
The cache needs to distinguish requests whenever their responses can differ. The URL and query string are common parts of the key, but they are not automatically sufficient. Selected headers, cookies, device context, and authorization context may also change the representation. A wrong key is not merely a cache-efficiency bug: it can leak one user's content to another.
The opposite mistake is also costly. Including every incidental header or cookie in the key can produce a huge number of variants, driving the hit rate down and increasing origin traffic. Decide which request inputs actually affect the representation, document that decision, and ensure the CDN's cache policy matches it. For private or highly personalized data, bypassing a shared cache is often safer than trying to encode all user state into a key.
Decision rule: Use cache keys deliberately when they make the delivery contract or an invariant easier to prove. If a key hides a representation-changing input, or only reduces configuration effort while obscuring an assumption, prefer the more explicit design.
3. TTL and revalidation
Freshness can be time-based or validated with ETag and Last-Modified. A long TTL improves hit rate and reduces origin work, but users may see old content longer. A short TTL gives the origin more control, but increases requests and can create a thundering herd when many edges recheck the same object at once.
Revalidation is useful when the cached object is likely still valid. The cache sends a validator to the origin, and the origin can respond that the object is unchanged rather than retransmitting it. stale-while-revalidate can serve a still-usable stale response while a background refresh occurs, trading bounded staleness for low latency and origin protection. The bound and the acceptable content age must be explicit; stale content is not automatically harmless.
Decision rule: Use TTL and revalidation deliberately when they make the freshness contract or an invariant easier to prove. If the policy hides how stale content may become or how the origin is protected, prefer the more explicit design.
4. Invalidation
Purging edge content globally can be slow and costly. It may also be difficult to reason about while requests are in flight or while different points of presence are processing the purge at different times. That makes a design based on “publish, then immediately purge everything” less predictable than it first appears.
Content-addressed or versioned asset URLs avoid invalidating immutable static files. A new build can publish a new filename or URL, such as one containing a content hash, while the old object remains safely cacheable until it ages out. The HTML or manifest that points to the asset still needs an appropriate freshness policy, and rollback or deletion behavior should be considered separately.
Decision rule: Use invalidation deliberately when it makes the freshness contract or an invariant easier to prove. If versioned immutable assets can solve the problem with less global coordination, prefer that approach and reserve purges for content that genuinely requires them.
5. Origin shielding
Without shielding, many edge locations can miss the same object and independently request it from the origin. A shield layer collapses those misses into fewer requests and protects the origin during cache expiry or a burst around a popular object. This is especially valuable when a large number of edges share an origin but the shield can retain a useful regional cache.
Shielding is another hop, so it can add latency on a miss and introduce another failure boundary. It also does not make an uncacheable response cacheable, and it does not replace request coalescing, rate limiting, or origin capacity planning. Choose the shield location and failure behavior with the origin's geography and reliability requirements in mind.
Decision rule: Use origin shielding deliberately when it makes the miss and origin-load invariant easier to prove. If it only adds a hop while hiding the actual source of origin traffic, prefer the more explicit design.
6. Edge compute
Workers and functions at the edge can perform lightweight routing, authentication checks, and personalization before a request reaches the origin. This can reduce latency for logic that depends only on request-local information. It can also normalize requests or select an origin without requiring a full application server round trip.
The boundary becomes less attractive as the logic needs state. Data locality, cold starts, observability, vendor limits, and consistency complicate stateful behavior. Authentication at the edge must still be correct and must not be confused with authorization for protected resources. Personalization also changes cacheability: if the worker produces user-specific output, that output must not accidentally enter a shared cache.
Decision rule: Use edge compute deliberately when it makes the routing, security, or latency contract easier to prove. If it introduces state, vendor coupling, or debugging difficulty without a clear requirement, prefer the more explicit origin-side design.
Worked example
Consider a large-scale distributed service whose requirements, traffic, failure modes, cost, and operational constraints must be made explicit. Start with the requirement in one sentence. Then list the input and output contracts and identify which of the concepts above owns each failure mode.
The important design move is separation. Parsing and validation belong at the boundary; domain rules belong in the domain or service layer; persistence rules belong in the database or repository; presentation rules belong in the client. Mixing these concerns can make a happy-path demo look shorter, but it makes stale data, malformed input, authorization mistakes, and partial failures much harder to reason about.
Use this conceptual request path as a starting point:
Client
|
DNS -> CDN / Edge
|
Load Balancer -> API instances -> Cache
| |
+------> Primary datastore
|
+------> Queue / Stream -> Workers
The diagram is intentionally simplified. DNS directs the client toward the service, while the CDN or edge decides whether it can satisfy the request from an edge cache or must continue inward. The load balancer distributes origin-side requests across API instances. The cache and primary datastore have different consistency and failure characteristics, and asynchronous work through the queue or stream should not be mistaken for an immediate response guarantee.
Walk through at least four cases:
- Normal path: identify the cache lookup, the origin response, and the headers or policy that determine whether the result can be stored and reused.
- Empty or missing value: define whether the system returns an empty representation, a cacheable not-found response, or an error, and make sure that choice cannot accidentally hide a later-created resource.
- Duplicate, retry, or concurrent path: where relevant, explain whether repeated requests can share work, whether writes are idempotent, and how simultaneous misses are prevented from overloading the origin.
- Dependency failure: describe what happens when the edge, cache, datastore, queue, or origin is unavailable. State whether the caller sees an error, a stale response, a fallback, or a delayed result.
For every case, state which layer detects the problem and what the caller observes. That level of explanation is expected in a senior code review or technical interview. It also gives an operator a useful starting point when metrics show a sudden drop in cache hits or a rise in origin errors.
Production perspective
Production correctness is broader than “the code works on my machine.” Ask how the design behaves during deploys, retries, partial failure, stale clients, concurrent requests, malformed data, schema changes, and high-cardinality traffic. A cache policy that is correct for one response variant can become unsafe after a new header, cookie, locale, or authorization rule is introduced.
Prefer explicit contracts, bounded resource usage, structured errors, and measurable behavior. Instrument cache hits and misses, age and freshness behavior, revalidation outcomes, purge propagation, shield traffic, origin latency, and error rates. Optimize only after you can identify the bottleneck or risk with evidence.
When the topic involves an external dependency, define a timeout and cancellation strategy. When it involves persistence, define transaction and consistency expectations. When it involves user-visible state, define loading, empty, error, stale, and success states. When it involves security, assume the client can be modified and all network input is untrusted. In particular, do not treat a cache hit or an edge decision as a substitute for server-side authorization.
Guided lab
Design delivery for a news site with static bundles, images, public articles, and personalized home pages. Specify cache keys, TTLs, invalidation or versioning, and which content must bypass a shared cache.
Your design should distinguish immutable build assets from content that editors update, and public article responses from user-specific home pages. For each category, explain what may be shared, how freshness is bounded, what happens during publication, and what the origin does when an edge misses.
Complete the lab with this discipline:
- Write the requirement and two non-requirements.
- List input, output, and error contracts before implementation.
- Implement the smallest correct vertical slice.
- Add at least one invalid-input test and one edge-case test.
- Instrument or inspect the behavior instead of guessing.
- Refactor one hidden assumption into an explicit type, constraint, function, or configuration.
- Explain one alternative design and why you did not choose it.
- Record a short “what would break at 10× scale?” note.
Edge cases and failure modes
For each concept, test absence, malformed input, duplicates, ordering or concurrency where applicable, and behavior at the smallest and largest credible sizes. Apply those tests to the actual delivery contract, not only to a helper function.
- Points of presence: test what happens when content is not present at an edge, when an edge cannot reach the next layer, and when different locations have different cache state. Include the smallest and largest credible geographic or traffic distributions.
- Cache keys: test missing and malformed query values, duplicate requests, ordering where request inputs are normalized, and concurrent requests for the same object. Verify that requests with different authorization, cookie, device, or other representation-changing context cannot share an unsafe response.
- TTL and revalidation: test absent or invalid freshness metadata, duplicate revalidations, ordering and concurrency around expiry, and objects at the smallest and largest credible TTLs. Verify both a fresh response and an origin response that says the representation is unchanged.
- Invalidation: test absent objects, malformed purge input, duplicate purge requests, ordering between publication and purge, concurrent reads during a purge, and the smallest and largest credible purge scope. Confirm what happens if a purge is delayed or only partially propagated.
- Origin shielding: test absent objects at both edge and shield, malformed origin responses, duplicate or concurrent misses, ordering around expiry, and the smallest and largest credible burst. Confirm that a shield failure does not silently create an uncontrolled origin stampede.
Common mistakes and debugging
- Solving the example instead of the requirement: a copied CDN pattern can be syntactically valid but architecturally wrong for private or personalized data.
- Hiding uncertainty with assertions, broad exception handlers, permissive schemas, or “temporary”
anyvalues. - Testing only the happy path and discovering the cache, freshness, and error contracts only after integration.
- Optimizing before measuring, or selecting a scalable mechanism without a scale requirement.
- Letting client-side behavior stand in for server-side authorization, validation, or persistence guarantees.
For debugging, reproduce the smallest failing case and inspect the actual request and response. Check the cache key, relevant headers, cookies, authorization context, age and freshness metadata, and whether the response was a hit, miss, stale result, or revalidation. Then trace the boundary where the invariant first became false: source or build, browser or DOM, Network or HTTP, server or route, database or query, or deployment or configuration.
Inspect the execution path and the metrics rather than guessing. A wrong user's response points first toward cache-key or authorization boundaries; unexpectedly old public content points toward TTL, validators, or invalidation; a sudden origin spike points toward misses, expiry coordination, or shield behavior. Fix the owning layer instead of adding a downstream patch that merely masks the symptom.
Interview questions
- What problem do Points of presence solve, and what trade-off or failure mode would make you choose a different approach?
- What problem do Cache keys solve, and what trade-off or failure mode would make you choose a different approach?
- What problem do TTL and revalidation solve, and what trade-off or failure mode would make you choose a different approach?
- What problem does Invalidation solve, and what trade-off or failure mode would make you choose a different approach?
- What problem does Origin shielding solve, and what trade-off or failure mode would make you choose a different approach?
Checkpoint
Without notes, explain CDNs, Edge Caching, Static Assets, and Edge Compute to another developer in five minutes. Include one invariant, one edge case, one production failure mode, and one alternative design. Then implement a small example without copying the lesson code.
Mastery checklist
- I can define the core terms precisely.
- I can choose a design from requirements instead of from habit.
- I can implement and test the normal path and edge cases.
- I can explain the runtime, storage, or complexity cost.
- I can identify which layer owns validation, errors, and recovery.
- I can compare at least two reasonable alternatives.
- I can explain how the design changes at larger scale or stricter reliability.
