Advanced HTTP Caching Strategies for Modern Web Apps (2026)

notes
A layered diagram showing browser cache, service worker, CDN edge, and origin server tiers

If you have read the basics of HTTP caching — Cache-Control headers, ETags, max-age — and want to know what comes next, this note is for you. Modern web applications involve multiple cache layers that interact in non-obvious ways, and the patterns that worked for static HTML do not scale to dynamic, personalized, or frequently-updated content.

This covers the advanced caching strategies that make a measurable difference for production web applications in 2026.

The Cache Stack

A request from a user’s browser passes through up to four caching layers:

  1. Browser memory cache — the fastest, but cleared on tab close
  2. Browser disk cache — persists across sessions, governed by Cache-Control headers
  3. Service worker cache — programmable, intercepted before the network request
  4. CDN edge cache — closest server to the user, shared across all users in a region

Each layer has different characteristics. Browser caches are per-user. CDN caches are shared. Service worker caches are programmable. The art is configuring them so they complement each other instead of fighting.

Stale-While-Revalidate in Practice

The stale-while-revalidate directive tells the browser: “serve the stale cached version immediately, but revalidate in the background.” The user gets an instant response, and the cache updates for the next request.

Cache-Control: max-age=60, stale-while-revalidate=3600

This says: the content is fresh for 60 seconds. After that, serve it stale for up to 3600 seconds while fetching a fresh copy in the background.

This pattern is ideal for content that changes regularly but where showing slightly stale data is acceptable — API listings, dashboards, news feeds. The user never waits for a network response, and the content is at most one request cycle out of date.

The catch: stale-while-revalidate does not guarantee the background revalidation completes before the user navigates away. If they visit the page, get stale content, and leave, the revalidation might not complete. The next visitor might still get stale content. For truly critical freshness requirements, this pattern is not sufficient.

Cache Partitioning

Modern browsers partition caches by top-level site to prevent cross-site tracking. This means that a cached resource loaded on example.com will not be served from cache when loaded on otherdomain.com, even if the URL is identical.

The practical impact: shared CDN-hosted libraries (jQuery from cdnjs, fonts from Google Fonts) no longer benefit from cross-site caching. Each site pays the full download cost for shared resources. This is a privacy win and a performance cost.

The strategy adjustment: self-host your third-party resources. If the cross-site cache benefit is gone, the only reason to load from a third-party CDN is convenience — and the security and privacy costs of third-party dependencies now outweigh it. Bundle the library, cache it with a content hash and immutable, and serve it from your own origin.

Service Worker Caching Strategies

Service workers give you programmatic control over caching. The major strategies:

Cache-first (offline-first). Check the cache, return the cached version if available, fetch from network only on cache miss. Best for static assets that rarely change.

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request)
      .then(cached => cached || fetch(event.request))
  );
});

Network-first. Try the network, fall back to cache on failure. Best for content that should be fresh when possible but available offline.

Stale-while-revalidate (SW version). Return from cache immediately, fetch from network in the background, update the cache. Similar to the HTTP header but with more control over the revalidation logic.

Cache-then-network. Show cached content immediately, then update the UI when the network response arrives. Best for data that changes frequently and where users benefit from seeing something instantly.

The key decision: service worker caching operates independently from HTTP caching. If you set a long max-age and also use a service worker with cache-first strategy, you have two layers that both prevent fresh content from reaching the user. Coordinate them deliberately or they will create confusing staleness bugs that are hard to diagnose.

CDN-Specific Patterns

Surrogate keys (cache tags). Some CDNs support tagging cached objects with keys, then purging by key. For example, tag all responses related to a product with product-42, and when the product changes, purge everything with that tag. This is more precise than URL-based purging and prevents over-invalidation.

Edge-side includes (ESI). Assemble pages at the CDN edge from cached fragments. The page shell is cached for a long time, and dynamic fragments (user menu, cart count) are fetched separately. This is an old pattern that has regained relevance with edge computing platforms.

Vary header discipline. The Vary header tells caches to store separate copies based on request header values. Vary: Accept-Encoding is standard. Vary: Cookie effectively disables shared caching (every user gets a different cached copy). Be precise with Vary — overly broad values fragment the cache and reduce hit rates.

Common Mistakes

Caching HTML with long max-age. If your HTML is cached for a day and you deploy a bug fix, users will see the old HTML loading new assets. Set short max-age for HTML (60 seconds or less) and long max-age for fingerprinted assets.

Forgetting about browser back/forward cache (bfcache). Browsers cache the entire page state when users navigate with back/forward buttons. Pages with certain features (unload event listeners, Cache-Control: no-store) are excluded from bfcache, causing noticeable slowness on back navigation.

Not testing cache behavior. Use curl -I to inspect actual response headers. Check that your CDN is returning the expected cache status headers. Verify that cache invalidation actually works — do not assume it does because the CDN docs say so.

The Practical Approach

Start with these defaults and adjust as needed:

  • Fingerprinted assets (JS, CSS, images with hashes): Cache-Control: public, max-age=31536000, immutable
  • HTML pages: Cache-Control: public, max-age=0, must-revalidate with strong ETags
  • API responses: Cache-Control: private, max-age=0 or stale-while-revalidate depending on staleness tolerance
  • CDN: purge on deploy for HTML, let fingerprinted assets expire naturally

Caching is one of those areas where the basics cover 90% of the benefit. The advanced strategies in this note cover the remaining 10% — which, at scale, is the difference between a good user experience and a great one.