By Rahul — Google Frontend Engineer
The Caching Layers
HTTP caching operates at multiple layers: browser memory cache, browser disk cache, service worker cache, CDN/proxy cache, and server-side cache. Each layer reduces latency differently.
Memory Cache vs Disk Cache
When you see 200 (from memory cache) in DevTools, the resource was found in RAM — instant access but cleared when you close the tab. 200 (from disk cache) means it was on disk — survives browser restarts but slightly slower to read.
// Browser decides where to cache based on:
// - Resource size (small → memory, large → disk)
// - Resource type (scripts often in memory for speed)
// - Available memoryFreshness Model
The browser determines if a cached resource is "fresh" using headers:
// Modern approach
Cache-Control: max-age=3600 // Fresh for 1 hour
// Legacy approach (still works)
Expires: Thu, 01 Dec 2025 16:00:00 GMT
// If both present, Cache-Control winsValidation Model
When a cached resource is stale, the browser validates it:
// Using ETag
Server sends: ETag: "v1.2.3"
Browser sends: If-None-Match: "v1.2.3"
Server responds: 304 Not Modified (if unchanged)
// Using Last-Modified
Server sends: Last-Modified: Mon, 01 Jan 2025 00:00:00 GMT
Browser sends: If-Modified-Since: Mon, 01 Jan 2025 00:00:00 GMT
Server responds: 304 Not ModifiedThe Vary Header Trap
// Server responds with:
Vary: Accept-Encoding
// Cache stores separate versions for gzip, br, identity
// This is normal and expected
Vary: Cookie
// Cache stores separate version per unique Cookie header
// This effectively disables caching for most cases!
Vary: *
// Never cache — every request is considered uniqueService Worker Cache
// The most powerful caching layer — you control everything
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then(cached => {
// Stale-while-revalidate pattern
const fetchPromise = fetch(event.request).then(response => {
const cache = caches.open('v1');
cache.put(event.request, response.clone());
return response;
});
return cached || fetchPromise;
})
);
});Best Practices
- Content-hash your static assets and cache immutably
- Always validate HTML — never set long max-age on it
- Use
stale-while-revalidatefor better UX on API responses - Be careful with
Vary— it can fragment your cache
Summary
HTTP caching works through freshness (max-age) and validation (ETag/Last-Modified). The browser manages memory and disk cache automatically. Service workers give you full cache control. Proper caching is the single biggest performance win for returning users.