Edge rendering architecture decides where HTML is produced, which responses can be shared, how long they may be reused, and what happens after underlying content changes. SSR, incremental static regeneration, and CDN purge are not interchangeable performance switches. Each makes a different freshness, availability, privacy, and origin-load promise. A fast cache that serves the wrong price, entitlement, or locale is a product defect.
Start from content classes and customer tolerance, not framework defaults. Separate public catalog pages, editorial content, regional availability, volatile inventory, account pages, and transaction confirmation. Define maximum staleness and failure behavior for each. Then select render timing, cache scope, key dimensions, invalidation trigger, and observability that can prove the promise under normal operation and origin failure.
Classify content by variability, sensitivity, and freshness
For each route or component, name authoritative sources, update frequency, legal or commercial consequence of staleness, personalization dimensions, traffic distribution, render cost, and acceptable failure. Public documentation may tolerate minutes of staleness and benefit from shared caching. A user's order state must not be stored in a public cache. A product page can mix a cacheable description with request-time price or inventory rather than forcing one policy across the whole response.
| Content class | Rendering default | Cache scope | Freshness and failure policy |
|---|---|---|---|
| Versioned assets and immutable releases | Build-time static | Long-lived public CDN with fingerprinted URL | Never mutate; publish new URL |
| Editorial and documentation | Static or ISR | Public CDN by locale and content generation | Serve bounded stale while regenerating |
| Public catalog | ISR or SSR with shared data cache | Public by market, currency, and representation | Event purge plus short revalidation |
| Volatile availability | Request-time or client/API fragment | Private or very short shared cache if safe | Prefer fresh; define degraded response |
| Account and transaction state | SSR or client fetch under authorization | Private/no-store unless proven otherwise | Never share; fail closed for sensitive state |
Apply HTTP cache semantics before framework features
RFC 9111 defines freshness, validation, cache keys, age, and response directives. Decide whether a response may be stored by a shared cache, whether it requires revalidation, and which request fields make representations differ. Cache-Control: private and no-store have distinct meanings. Vary can prevent representation confusion, but varying on high-cardinality headers or cookies can destroy hit rate and create unbounded objects.
Use validators such as strong or weak entity tags according to representation semantics and support conditional requests to avoid transferring unchanged content. Inspect the complete delivery path: browser, service worker, reverse proxy, framework data cache, full-route cache, and CDN may all hold different objects with different clocks. Document which layer owns each TTL and invalidation. A purge at one layer does not prove every downstream copy disappeared.
Choose static generation, ISR, or request-time SSR deliberately
Static generation produces artifacts before requests and suits finite content whose publication can trigger a build. ISR keeps a previously generated representation and regenerates according to time or an event. Request-time SSR computes a response for the request and is appropriate when inputs or authorization demand it. Edge execution can reduce network distance, but runtime limits, data-source distance, cold starts, and replication consistency still shape latency.
The Next.js ISR guide illustrates time-based and on-demand revalidation, including that a request after expiry can receive stale content while regeneration runs and that failed regeneration preserves the last successful result. These are product semantics. Decide whether the next writer must read its update immediately, whether a first request may pay generation latency, and whether stale output is safe during repeated failures.
| Strategy | Strength | Failure mode | Best control |
|---|---|---|---|
| TTL only | Simple and resilient to missed events | Stale until expiry; synchronized expiry load | Jitter plus bounded maximum staleness |
| Path purge | Easy mental model for one URL | Aliases and dependent pages remain | Canonical route registry |
| Tag or surrogate-key purge | Invalidates related objects across routes | Incorrect tagging causes over- or under-purge | Versioned dependency map and audit |
| Generation key | New namespace makes old content unreachable | Doubles cache during transition | Explicit rollout and reclamation |
| Synchronous fresh render | Read-after-write can be immediate | Origin latency and availability on request path | Timeout, load shedding, and scoped use |
Design invalidation as a versioned delivery workflow
Emit invalidation only after authoritative data commits. Include stable event identity, source version, affected entity, tenant or market scope, and change time. Map entities to cache tags or surrogate keys at render time so a product update can invalidate product, category, search, and related fragments without guessing URLs later. Consumers must be idempotent; retries are expected. Record receipt, dispatch, provider acknowledgement, and observed fresh version.
Purge propagation is not instantaneous or necessarily atomic across regions. For high-consequence changes, combine purge with version checks or generation keys and verify representative points of presence. Coalesce simultaneous misses and cap regeneration concurrency to prevent a purge storm from overwhelming data sources. Warm only valuable objects under a budget. If invalidation is unavailable, TTL remains the backstop; choose it from tolerated stale duration, not an arbitrary day.
Bound stale serving and degraded behavior
RFC 5861 defines stale-while-revalidate and stale-if-error extensions. They can hide regeneration latency or preserve availability during an origin failure, but only within declared windows. Do not use stale-if-error for revoked access, corrected safety information, rapidly changing price commitments, or content that must disappear by deadline. A stale response must still satisfy the original authorization and cache partition.
Define behavior after the stale window: wait for origin, return a controlled error, show a reduced shell, or omit a volatile fragment. Make stale status observable to support and, when material, to the UI. Avoid silent indefinite stale serving caused by resetting age during cache-to-cache transfer. Test origin timeouts, failed regeneration, invalid payload, partial regional purge, and clock skew. Recovery should not replace a good cached object with an error page.
Observe correctness, not only hit ratio
Record cache status, age, generation or source version, key dimensions, render location, revalidation result, purge lag, origin load, and response latency without logging sensitive key values. RFC 9213 standardizes a Cache-Status response field that caches can use to describe handling. Keep internal diagnostics access-controlled because cache topology and keys may expose operational detail.
Define service indicators for fresh-enough responses by content class, cross-user leakage, purge completion, regeneration success, and origin saturation. Run synthetic publish-to-observe probes: update a canary entity, send the normal invalidation, and measure when every sampled region returns the expected version. Audit cache keys for locale, market, currency, device representation, experiment, and authorization dimensions. A high hit rate is valuable only after correctness is established.
Review cache keys and personalization boundaries
Write a key schema for every shared object: canonical URL, host, scheme where relevant, locale, market, currency, content encoding, device representation, experiment, generation, and only the authorization dimensions that have been proven shareable. Normalize query parameters and reject unknown cache-key inputs rather than allowing trackers to create unbounded variants. Verify that redirects and error responses use equally safe partitioning; leaks often appear outside the successful page.
Test two-user sequences against the same edge location: privileged then anonymous, account A then account B, one market then another, experiment treatment then control, and authenticated error then public request. Inspect response body, headers, age, cache status, and cookies. Purge and regeneration tests should repeat these sequences because a safe warm key can become unsafe when a fallback omits one dimension. Automate the suite for every caching-configuration change.
Review browser caching separately from CDN caching. A successful edge purge cannot remove a fresh object already stored in a customer's browser or service worker. For content that may require urgent withdrawal, use short browser freshness, validation, or versioned URLs while allowing a longer shared-cache strategy where appropriate. Test back navigation and offline restoration so application code does not display an older client-side snapshot after the network has delivered a corrected representation.
Key takeaways
- Classify content by sensitivity and tolerated staleness before selecting SSR, ISR, or static rendering.
- Apply HTTP storage, freshness, validation, and variation semantics across every cache layer.
- Publish committed versioned invalidations and map entities to tags when rendering.
- Bound stale-while-revalidate and stale-if-error to content where old output remains safe.
- Measure fresh-enough correctness, purge propagation, and origin protection alongside hit rate and latency.
FAQ
Is ISR always executed at the CDN edge?
No. ISR describes regeneration and caching behavior; execution location depends on framework and deployment platform. Verify runtime support, cache coordination, data-source distance, and multi-instance invalidation in the actual environment.
Why not purge the entire CDN after every publication?
Global purge is simple but destroys useful cache residency, raises origin load, increases latency, and can turn routine publication into an availability event. Use entity tags or versioned generations with tested dependency mapping, reserving broad purge for exceptional recovery.
Can personalized pages be cached?
Private browser caches can often store appropriately controlled responses. Shared caching requires rigorous partitioning and proof that no user-specific or authorization-sensitive data can cross keys. Frequently, a public shell with private fragments is safer and more efficient.
Conclusion
A sound edge architecture turns freshness into an explicit product contract. Content classification, HTTP semantics, deliberate rendering mode, versioned purge, bounded stale behavior, origin protection, and regional proof make performance improvements dependable. The result is not merely a faster page; it is a page whose age and audience the system can explain.