DEV Community

137Foundry
137Foundry

Posted on

How HTTP Caching Works at the Browser, CDN, and Proxy Layer

HTTP caching is not one thing. It is a set of behaviors that happen at different layers of the network stack, each governed by the same response headers but producing different effects depending on which layer is doing the caching.

Understanding each layer separately makes it much easier to diagnose caching problems, because a bug that looks like "the browser is not caching this" is often actually "the CDN is stripping the header before it reaches the browser." Treating all caching as one system obscures the actual source of the problem.

The Three Cache Layers

A typical web request passes through three caches on its way from origin server to user.

The browser cache is local to the user's device. It stores responses that the server marks as cacheable, keyed by URL. Subsequent requests for the same URL check this cache first. If the stored response is still fresh, the request never leaves the device.

The CDN edge cache is a shared cache maintained by your CDN provider at geographic nodes distributed around the world. When a user requests a resource, the CDN node closest to them checks whether it has a cached copy. If it does, it serves the response directly. If not, it fetches from the origin and caches the response for future requests in that region.

Intermediate proxies sit between the user and the CDN, or between the CDN and origin, depending on network topology. Corporate networks often include forward proxies that cache responses on behalf of internal users. These proxies also consult Cache-Control directives.

How Freshness Works

A cached response has a lifetime. The server signals how long the response should be considered fresh via the max-age directive in the Cache-Control header.

Cache-Control: max-age=3600 means the response is fresh for 3,600 seconds after it was received. During that window, caches at any layer can serve the stored response without consulting the server.

After the window expires, the response is stale. A stale response can still be served in some cases, but the cache should attempt to revalidate it first. Revalidation sends a conditional request to the server: "I have this response from earlier. Has anything changed?"

The server responds either with 304 Not Modified, which means the cached copy is still valid and can be served, or with a full 200 OK response containing the updated content.

Freshness applies independently at each cache layer. A browser cache entry might expire before a CDN cache entry for the same resource, or vice versa, depending on how long the resource has been cached at each layer.

How the Browser Cache Decides What to Store

The browser caches a response if the response headers permit it. The decision involves several rules:

  • The request method must be GET or HEAD. POST responses are not cached.
  • The response status must be cacheable (200, 301, 302, 404, and a few others are cacheable by default).
  • The response must not include Cache-Control: no-store.
  • If Cache-Control: private is present, the response is cached only in the browser, not in shared caches.
  • If no Cache-Control header is present, the browser may cache heuristically based on Last-Modified.

When a cached response becomes stale, the browser sends a conditional request. If the response included an ETag header, the browser sends If-None-Match: "etag-value". If the response included Last-Modified, the browser sends If-Modified-Since: timestamp. The server responds with 304 if the resource has not changed, allowing the browser to extend the life of its cached copy.

browser network waterfall showing cached and uncached resources
Photo by Markus Spiske on Pexels

How the CDN Cache Differs From the Browser Cache

CDN caches are shared: they store responses that are served to many different users. This introduces considerations that browser caches do not have.

Personalization. The CDN must not store responses that differ by user. Cache-Control: private tells CDNs to skip the response. Without this directive, a CDN might cache a personalized response and serve it to the wrong user.

Cache keys. CDNs cache by URL by default. If a response varies by request header (e.g., different responses for mobile vs. desktop based on User-Agent), the Vary header must be included so the CDN stores separate entries.

TTL differentiation. s-maxage lets you specify a TTL for shared caches independently of the browser TTL. Cache-Control: max-age=60, s-maxage=3600 gives the browser a 1-minute freshness window and the CDN a 1-hour window. This pattern is useful for resources where you want the CDN to cache aggressively but the browser to check for updates more often.

Purging. Unlike browser caches, CDN caches can be cleared server-side. Most CDNs offer an API to purge cached entries by URL, path prefix, or tag. This enables cache invalidation as part of a deployment pipeline.

How Intermediate Proxies Behave

Intermediate proxies follow the same HTTP caching spec as CDNs. They respect Cache-Control: private to avoid storing personalized responses, and they honor no-store to skip caching entirely.

The main practical difference is that you typically cannot predict which proxies a request might pass through, and you cannot purge their caches. A corporate proxy that caches a response with a long TTL will continue serving that response until the TTL expires, regardless of what happens on your origin.

The Cache-Control: no-cache directive is useful here. It allows responses to be stored in intermediate caches but requires revalidation before serving. This means even a proxy that has cached the response for a long time will check with the server before serving it to a new request.

How Service Workers Interact With HTTP Caching

Service workers add a programmable cache layer between the browser and the network. They can intercept fetch requests, serve responses from their own cache, and bypass HTTP caching entirely.

A service worker's cache is independent of the browser's HTTP cache. A resource cached by a service worker may be served even when the HTTP cache would have revalidated or rejected the cached copy.

This means HTTP caching behavior and service worker behavior can conflict. If you are debugging a caching issue in an application that uses a service worker, check whether the service worker is intercepting the request before checking the HTTP cache configuration.

server and CDN cache architecture diagram concept
Photo by Brett Sayles on Pexels

The Practical Takeaway

Each cache layer operates independently but responds to the same response headers. Cache-Control is the single header that controls all of them, with directives that target different layers specifically.

The most reliable approach to multi-layer caching:

  • Use public, max-age=31536000, immutable for static assets with content-addressed URLs
  • Use no-cache for HTML pages so browsers revalidate but CDN caches work with short TTLs
  • Use private, no-store for personalized API responses
  • Use s-maxage to differentiate CDN and browser TTLs when needed
  • Add Vary for any response where content differs by request header

For more detail on how these directives interact with CDN behavior and deployment workflows, 137Foundry covers caching configuration as part of web application delivery at our services page. The full article HTTP Caching: A Practical Guide for Web Developers covers each directive in depth. The HTTP caching RFC at the IETF is the authoritative specification if you need to resolve ambiguous behavior. Google's web.dev caching guide is the most accessible reference for practical application. For CDN-specific caching behavior and how edge nodes modify response headers, Cloudflare's caching documentation is the most detailed public reference for a widely deployed CDN.

developer reviewing web performance metrics on dashboard
Photo by Daniil Komov on Pexels

Top comments (0)