TL;DR — Quick Summary
Caching stores resources locally to avoid re-downloading them on every visit. Use `Cache-Control: max-age=31536000, immutable` for versioned static assets (JS, CSS with content hashes) and `Cache-Control: no-cache` with ETag for HTML pages. CDN caching reduces TTFB from 200–500ms to 10–50ms globally. The `stale-while-revalidate` directive serves cached content instantly while updating in the background. Proper multi-layered caching (browser + CDN + server-side) reduces return-visit load times from seconds to under 500ms, cuts bandwidth costs by 60–80%, and directly improves LCP, FCP, and TTFB. Approximately 40% of websites still have suboptimal cache headers — fixing this is one of the easiest, highest-impact performance wins available.
What is Caching / HTTP Caching?
Caching is the process of storing copies of web resources at intermediate points between the origin server and the end user, allowing subsequent requests to be served faster without re-fetching from the origin. It operates at multiple complementary levels, and understanding each layer is essential for implementing an effective caching strategy.
Browser Cache (HTTP Cache): The browser's built-in cache stores responses locally based on HTTP headers. When a user revisits a page, the browser checks its cache before making network requests. `Cache-Control: max-age=31536000, immutable` tells the browser to cache a resource for one year and never revalidate — ideal for versioned static assets with content hashes in their filenames (e.g., `main.a1b2c3.js`). `Cache-Control: no-cache` caches the response but forces revalidation with the server on every request using ETag or Last-Modified headers — ideal for HTML documents that may change.
CDN Cache (Edge Cache): A Content Delivery Network distributes cached content across hundreds of global Points of Presence (PoPs). When a user requests a resource, DNS routes them to the nearest CDN edge server. If the resource is cached (cache hit), it's served in 10–50ms instead of 200–800ms from the origin. CDN caching is the single most effective way to reduce TTFB for geographically diverse users.
Server-Side Cache: Server-side caching operates at two levels: (1) page cache stores complete rendered HTML pages, eliminating the need to execute PHP/Node/Python and query databases on every request, and (2) object cache (Redis, Memcached) stores database query results, computed values, and session data in memory for sub-millisecond retrieval. On WordPress, WooCommerce, and other database-driven platforms, server-side caching can reduce server response time from 2–5 seconds to under 100ms.
Service Worker Cache: Service workers are JavaScript files that run in a separate browser thread, intercepting network requests and implementing custom caching strategies. They enable: cache-first (serve from cache, fall back to network), network-first (try network, fall back to cache), and stale-while-revalidate (serve cached, update in background). Service workers are the foundation of Progressive Web Apps (PWAs) and offline-first experiences.
Key HTTP Cache Headers:
- •`Cache-Control` — The primary caching directive. Key values: `max-age` (cache duration in seconds), `no-cache` (cache but revalidate), `no-store` (never cache), `immutable` (never revalidate), `stale-while-revalidate` (serve stale while updating), `public`/`private` (CDN cacheability).
- •`ETag` — A content fingerprint (hash) used for conditional requests. The browser sends `If-None-Match: [ETag]` to check if content changed. Server responds with 304 Not Modified (no body) if unchanged.
- •`Last-Modified` — Timestamp-based validation. Browser sends `If-Modified-Since` header. Less precise than ETag but simpler.
- •`Vary` — Specifies which request headers affect the cached response. `Vary: Accept-Encoding` caches separate versions for gzip/Brotli. `Vary: Cookie` caches per-user responses (careful: can destroy cache hit rates).
Caching / HTTP Caching Thresholds
| Metric | Good | Needs Improvement | Poor |
|---|---|---|---|
| CDN Cache Hit Ratio | > 90% | 70–90% | < 70% |
| Static Asset max-age | 31536000 (1 year) | 86400–2592000 | < 86400 (1 day) |
| TTFB (CDN-cached) | < 50ms | 50–200ms | > 200ms |
| Return Visit Load Time | < 1s | 1–3s | > 3s |
| Lighthouse Cache Audit | 0 warnings | 1–5 warnings | > 5 warnings |
| Server Page Cache Hit Rate | > 85% | 60–85% | < 60% |
| Bandwidth Savings (CDN) | > 70% | 40–70% | < 40% |
Google evaluates the 75th percentile (p75) of real-user field data over a rolling 28-day window.
History & Evolution
Caching has been fundamental to web performance since the early days of the web, evolving from simple browser storage to sophisticated multi-layer strategies.
Timeline of key milestones:
- •1996 — HTTP/1.0 introduces `Expires` header for basic time-based caching and `Pragma: no-cache` for cache prevention.
- •1997 — HTTP/1.1 (RFC 2068) introduces the `Cache-Control` header, providing granular cache directives that supersede the limited `Expires` header.
- •1999 — `ETag` (entity tag) is standardized in RFC 2616 for content-based cache validation, enabling conditional requests that return 304 Not Modified responses.
- •2004 — Akamai and early CDN providers popularize edge caching, reducing origin server load and improving global performance.
- •2010 — Cloudflare launches with a free CDN tier, democratizing edge caching for small and medium websites. CDN adoption begins rapid growth.
- •2014 — `stale-while-revalidate` directive proposed, enabling cached content to be served immediately while updating in the background — a breakthrough for balancing freshness and speed.
- •2015 — Service Workers ship in Chrome and Firefox, enabling JavaScript-controlled caching with offline support. This enables Progressive Web Apps (PWAs) with granular cache strategies.
- •2016 — The `immutable` Cache-Control directive is proposed and quickly adopted by major browsers. It prevents unnecessary revalidation of versioned assets, eliminating 304 round-trips.
- •2019 — `stale-while-revalidate` gains wide browser support, becoming the standard approach for semi-dynamic content caching.
- •2020 — Cache partitioning begins rollout in Chrome (later Safari and Firefox). Third-party caches are partitioned by top-level domain for privacy, ending cross-site cache sharing.
- •2022 — Modern build tools (Vite, Webpack 5, esbuild) make content-hash filenames the default, making immutable caching trivial to implement.
- •2025–2026 — Multi-layered caching is standard practice: content-hashed immutable browser caching + CDN edge caching + `stale-while-revalidate` for dynamic content + optional service worker for offline. Shared dictionary compression (Zstandard) begins reducing delta transfer sizes between cached and updated versions of resources.
How Caching / HTTP Caching is Measured
Cache effectiveness is measured through a combination of cache hit ratios, resource loading analysis, and HTTP header inspection. Understanding what to measure and which tools to use is critical for diagnosing caching issues and validating improvements.
Cache Hit Ratio (The Primary KPI): The cache hit ratio measures the percentage of requests served from cache vs. origin. A CDN with 95% cache hit ratio serves 95 out of 100 requests from edge — only 5 require origin fetches. Target: > 90% for static assets, > 70% for dynamic content with edge caching.
Formula: Cache Hit Ratio = (Cache Hits / Total Requests) × 100
Browser Cache Analysis: Chrome DevTools Network panel shows cache status for every resource. The 'Size' column displays 'disk cache' (cached on disk), 'memory cache' (cached in RAM from earlier in session), or the actual transfer size (fetched from network). Filter for resources served from network that should be cached.
First View vs. Repeat View: WebPageTest's 'Repeat View' test loads the page twice — First View (empty cache) and Repeat View (warm cache). The difference quantifies your caching benefit. A well-cached site should load 60–80% faster on Repeat View.
Lighthouse Cache Audit: The 'Serve static assets with an efficient cache policy' audit identifies resources with short or missing cache headers. It recommends a minimum cache duration of 1 year (31536000 seconds) for versioned static assets.
CDN Dashboard Metrics: All major CDNs provide dashboards showing:
- •Cache hit ratio (overall and per-resource)
- •Bandwidth saved (cache hits × resource size)
- •Origin offload percentage
- •Cache purge frequency and latency
- •Geographic distribution of cache hits
The Practical Measurement Workflow:
- 1Check Lighthouse cache audit → identify resources with suboptimal cache headers.
- 2Inspect Cache-Control headers in Chrome DevTools → understand current caching policy.
- 3Run WebPageTest First View vs. Repeat View → quantify the caching benefit.
- 4Check CDN dashboard → verify cache hit ratio > 90%.
- 5Monitor over time → ensure cache headers aren't regressing after deployments.
Key rule: Field data (CrUX) determines Google rankings. Lab data (Lighthouse, WebPageTest) is for debugging and iteration.
Common Causes of Poor Caching / HTTP Caching Scores
Poor caching performance has specific, diagnosable causes that fall into several categories:
Missing or Misconfigured Cache Headers:
- •No `Cache-Control` header on static assets — forces browsers to use heuristic caching (unpredictable behavior, typically 10% of the `Last-Modified` age).
- •Short `max-age` on versioned static assets — setting `max-age=3600` (1 hour) for JS/CSS forces unnecessary revalidation every hour, adding 50–200ms per resource per visit.
- •`Cache-Control: no-store` applied globally — over-cautious policy prevents all caching, even for public static resources.
- •Missing `immutable` directive on content-hashed assets — causes browsers to revalidate on navigation even when the content hash hasn't changed.
CDN Caching Issues:
- •`Vary: Cookie` in server responses — fragments CDN cache into per-user variations, effectively preventing CDN caching. Common on WordPress sites with session cookies.
- •`Set-Cookie` headers on static assets — CDNs won't cache responses that set cookies. Ensure static asset responses don't include session cookies.
- •`Cache-Control: private` on public resources — prevents CDN caching. Only appropriate for user-specific content.
- •Query string variations — URLs like `style.css?v=1` and `style.css?v=2` are cached as separate entries. Use content-hash filenames instead.
- •Low-traffic pages — CDN cache entries expire before being reused. Consider longer CDN TTLs or origin-shield configuration.
Server-Side Caching Issues:
- •No page caching on dynamic sites — every request executes PHP/Node/Python and queries the database, even for identical content.
- •Redis/Memcached not configured — database queries run on every request instead of serving cached results.
- •Cache invalidation failures — stale cached pages persist after content updates, showing outdated information.
- •Overly aggressive purging — cache purges on every edit clear the entire cache instead of just affected pages.
Build Tool & Deployment Issues:
- •No content hashes in filenames — using `style.css` instead of `style.a1b2c3.css` makes cache busting impossible without changing max-age.
- •Cache headers reset after deployment — CI/CD pipelines or hosting platforms may override custom cache headers.
- •Different cache headers in development vs. production — local dev servers often set `Cache-Control: no-cache` on everything.
Diagnostic tip: Start with Lighthouse's 'Serve static assets with an efficient cache policy' audit to identify the most impactful issues. Then use Chrome DevTools Network panel to inspect actual Cache-Control headers on each resource. Check your CDN dashboard for cache hit ratio — if it's below 80%, investigate `Vary` headers and `Set-Cookie` responses.
Frequently Asked Questions
`no-cache` still caches the response but forces the browser to revalidate with the server on every request using ETag or Last-Modified headers. The server responds with 304 Not Modified (no body) if unchanged — fast and efficient. `no-store` prevents any caching whatsoever — the full response must be downloaded on every request. Use `no-cache` for HTML documents that may change; use `no-store` only for sensitive data (banking pages, personal health information) that should never be stored locally.
Versioned static assets with content hashes in filenames (e.g., `main.a1b2c3.js`): cache for 1 year with immutable (`max-age=31536000, immutable`). This is safe because the hash changes when the file changes. Unversioned images: 1 month to 1 year depending on change frequency. Fonts: 1 year (they rarely change). HTML documents: use `no-cache` with ETag for revalidation, or short `max-age` with `stale-while-revalidate`.
Cache busting forces browsers to download a new version of a cached resource. It's needed because browsers aggressively cache resources based on URL — if the URL doesn't change, the browser serves the cached version. The best approach is content-based filenames: `style.abc123.css` becomes `style.def456.css` when the content changes. This creates a new URL, bypassing the old cache. Avoid query-string cache busting (`style.css?v=2`) — some CDNs and proxies don't cache URLs with query strings.
The `immutable` Cache-Control directive tells the browser that the resource will never change at this URL — so it should skip revalidation entirely, even on navigation (when browsers typically revalidate cached resources). Without `immutable`, browsers may send conditional requests (If-None-Match) on navigation, adding 50–200ms per resource round-trip. Only safe to use with content-hashed filenames where the URL changes when the content changes.
`stale-while-revalidate` is a Cache-Control directive that serves the cached response immediately (even if slightly stale) while fetching an updated version in the background. Example: `Cache-Control: max-age=60, stale-while-revalidate=3600` — serves cached content instantly for 60 seconds. After 60 seconds, it still serves the stale version but triggers a background update. The next request gets the fresh version. Combines the speed of aggressive caching with the freshness of frequent updates.
Yes — caching significantly impacts multiple Core Web Vitals. CDN caching reduces TTFB (a prerequisite for fast LCP) from 200–800ms to 10–50ms. Browser-cached resources (CSS, fonts, images) load from disk in 1–5ms instead of 200–500ms, directly improving LCP and FCP for return visitors. Server-side page caching eliminates 1–5 seconds of server processing, improving TTFB. Google evaluates the p75 of real-user CWV data — return visitors with cached assets pull this distribution toward 'good' thresholds.
- 1Chrome DevTools Network panel: check the Size column — 'disk cache' or 'memory cache' means cached; an actual size means network-fetched. 2) Inspect Cache-Control and ETag response headers for each resource. 3) Use the 'Disable cache' checkbox to simulate first-time visitors. 4) WebPageTest Repeat View shows warm-cache behavior. 5) CDN dashboard shows cache hit ratio — if below 80%, check for Vary: Cookie, Set-Cookie, or Cache-Control: private issues. 6) Use RedBot.org for detailed header analysis.
Modern browsers (Chrome since v86, Firefox, Safari) partition caches by top-level site for privacy. This means a CDN-hosted library (like Google Fonts or jQuery from a CDN) cached while visiting site-a.com won't be reused when visiting site-b.com — even if it's the exact same URL. This privacy feature eliminates cross-site cache sharing. The practical implication: self-hosting critical resources (fonts, JS libraries) is now preferred over CDN-hosted shared resources.
Browser caching stores resources on the user's device — it only helps that specific user on that specific device. CDN caching stores resources at edge servers worldwide — it helps all users in a geographic region. CDN cache reduces TTFB for everyone; browser cache reduces load time only for returning visitors. Both are essential: CDN caching for global performance + browser caching for return-visit speed. They work together — CDN serves the first request, browser caches it for subsequent requests.
Page cache stores the complete rendered HTML output of a page — the entire response. When a cached page is requested, the server skips all PHP/Node execution and database queries, serving pre-rendered HTML instantly. Object cache (Redis, Memcached) stores individual data objects (database query results, API responses, computed values) in memory. If the page cache misses (personalized page, cache expired), object cache still speeds up the database-heavy parts of page generation by 80–95%.
For step-by-step optimization, platform-specific fixes, code examples, and case studies, read our full guide:
The Ultimate Guide to Web Hosting, CDN & Infrastructure for Speed: TTFB, Caching, Edge Workers & Traffic Resilience in 2026Struggling with Caching / HTTP Caching?
Request a free speed audit and we'll identify exactly what's holding your scores back.