Exactly which representation is stored: HTML, RSC payload, API response, query result, or asset?
Lesson 03 · Advanced frontend rendering
Cache is a correctness policy
Estimated time: about 14 minutes
Caching decides which past representation may stand in for the present, for which audience, and for how long. Performance is the consequence; correctness is the design.
Every cache needs four answers
Which inputs distinguish entries: URL, locale, region, device, authorization, experiment, or tenant?
How long may the value be reused without checking its source?
What expires, validates, invalidates, refreshes, or safely serves stale data?
correct cache = correct value × complete key × accepted staleness × reliable invalidation
A page is cached more than once
Framework discussions often say “the cache” while several independent stores are active. Each has its own key, lifetime, and purge mechanism.
Private HTTP cache
One user agent. Can safely retain some personalized responses when marked private. History and client-router caches have separate semantics.
Shared CDN cache
One entry can serve many users. Excellent leverage—and the most dangerous place to omit tenant, locale, or personalization from the key.
Render/output cache
Stores generated HTML or framework payloads. ISR and prerender caches live here, sometimes coordinated across instances.
Query/object cache
Stores fetches, database results, or computed objects. A fresh page render can still consume stale data from this layer.
Stale-while-revalidate, request by request
This simulation uses a 60-second fresh lifetime plus an allowed stale window. Notice that regeneration is usually triggered by traffic, and that the request crossing the boundary may still receive the old representation.
t = 0s
Walk through one cache entry.
Requests /article
Empty
missGenerates version 1
MISS — version 1 is returned and cached for 60 seconds.
Use the verbs precisely
Store
Retain a response. Storage does not imply it can be reused without a check.
Reuse while fresh
Serve without contacting the origin because age remains within the declared lifetime.
Validate
Ask whether a stored representation is still current, commonly with an ETag or Last-Modified conditional request.
Invalidate
Mark or remove entries after a known change. This must reach every layer that can still answer independently.
The naming trap: HTTP no-cache permits storage but requires validation before reuse. no-store asks caches not to store the response. private permits a private cache but excludes shared caches.
What can go wrong
| Failure | Mechanism | Senior response |
|---|---|---|
| Data leak | Personalized output stored under a shared key | Make it private or remove personalization; audit every key dimension. |
| Stale correctness | Display cache treated as transactional truth | Revalidate price, permissions, and inventory during the mutation. |
| Split-brain cache | One instance or layer receives invalidation; another does not | Use shared tag state, purge CDN variants, and observe propagation. |
| Cache stampede | Many misses regenerate the same expensive entry | Coalesce requests, jitter expiry, refresh early, or serve bounded stale data. |
| Key explosion | High-cardinality cookies or headers fragment shared entries | Normalize intentional variants; avoid broad Vary dimensions. |
| Permanent stale | Invalidation event is lost and TTL is effectively infinite | Combine event invalidation with a bounded backstop expiry. |
What ISR really buys
Incremental Static Regeneration reuses prerendered output and regenerates it after time or an explicit event. It is a useful implementation of cached rendering—not a guarantee of immediate freshness.
Good fit
Large public page sets, expensive rendering, high cache reuse, and bounded staleness that the product accepts.
Poor fit
User-specific output, strict read-after-write expectations, authorization decisions, or values that must be current for a transaction.
Time revalidation
Simple backstop, but refresh happens whether or not content changed and may begin only after later traffic arrives.
Event revalidation
Precise when reliable, but requires dependency tags, delivery guarantees, and coordination with outer CDN caches.
Architecture drill
Scenario: Product copy changes hourly. Regional prices change each minute. Signed-in customers can receive private discounts. Stock may be approximate on the page, but an order must never confirm unavailable inventory. What policy should lead your answer?
Choose first. Then assign each field a cache audience, key, staleness budget, invalidation path, and transactional safeguard.
A senior-shaped interview answer
“I would cache by data class rather than choose one policy for the route. Product copy is public and stable, so I’d cache it broadly and invalidate by product tag. Regional price needs a normalized region key and a much shorter freshness budget.”
“Account discounts must not enter a shared representation. I’d render or fetch them through a private boundary. Stock on the page can be a bounded-stale signal, but checkout must atomically re-check or reserve inventory—the display cache is never the authority.”
“I’d verify cache keys, Age and Cache-Status headers, hit ratio, regeneration latency, purge propagation, stale-serving counts, and origin load. In a multi-instance deployment, tag invalidation and any upstream CDN purge need explicit coordination.”
Primary reading
- RFC 9111: HTTP Caching — authoritative definitions for freshness, reuse, validation, keys, and stale responses.
- MDN: Cache-Control — practical directive semantics, especially
private,no-cache,no-store, ands-maxage. - Next.js: Incremental Static Regeneration — concrete request-triggered regeneration behavior.
- Next.js: CDN caching — why framework invalidation and outer CDN invalidation can diverge.