# Caching Strategies > Layer browser, CDN, and app caches to serve responses without re-fetching. Frontend architecture guidance from fearchitect, written by Abas Turabli and last reviewed 2026-06-21. Source: https://fearchitect.com/topics/caching-strategies Use it as reference for the task at hand. Before changing code, check this guidance against the codebase: where the code already makes a different, deliberate choice, flag the conflict instead of rewriting it. Library APIs move faster than this guide, so confirm exact signatures in the official docs linked at the end. ## Summary HTTP caching, CDN edge caches, and service workers form a layered system. Each layer intercepts requests before they hit the origin. Getting the directives right — especially distinguishing `max-age` from `s-maxage`, or `no-cache` from `no-store` — determines how fresh users' data is and how little your origin pays per request. ## Definition Caching stores a response and reuses it for matching requests. The three main layers are: - **Browser cache** — per-user, governed by HTTP `Cache-Control` response headers and `ETag`/`Last-Modified` validators. - **CDN / shared cache** — sits between origin and many users; respects `s-maxage` and `Surrogate-Control`. One cached response serves thousands. - **App cache (service worker)** — JavaScript-controlled, survives offline; you pick the strategy per route. ## Cache-Control directives | Directive | Who it targets | What it does | | --- | --- | --- | | **max-age=N** | Browser | Cache for N seconds; no revalidation within TTL | | **s-maxage=N** | CDN only | CDN TTL; overrides max-age for shared caches | | **immutable** | Browser | Skip revalidation even on hard refresh | | **no-cache** | Browser + CDN | Store, but revalidate before every use | | **no-store** | Browser + CDN | Never store; use for sensitive data | | **stale-while-revalidate=N** | Browser + CDN | Serve stale instantly; refresh in background within N seconds | ## Service-worker stale-while-revalidate Returns the cached response immediately if present, then refreshes in the background. The next request gets the updated copy. **Service-worker SWR strategy** ```ts // sw.ts — SWR strategy for API routes self.addEventListener("fetch", (event: FetchEvent) => { if (!event.request.url.includes("/api/")) return; event.respondWith( caches.open("api-v1").then(async (cache) => { const cached = await cache.match(event.request); // Fetch in background — update cache for next request const networkFetch = fetch(event.request).then((res) => { if (res.ok) cache.put(event.request, res.clone()); return res; }); // Serve cached instantly if available; otherwise wait for network return cached ?? networkFetch; }) ); }); ``` No framework dependency — works in any service worker. Versioning the cache name (`api-v1`) lets you invalidate old entries when the SW updates. ## Diagram ```mermaid sequenceDiagram participant Browser participant SW as Service Worker participant CDN participant Origin Browser->>SW: GET /page alt Cached & fresh SW-->>Browser: Response from cache else Stale or miss SW->>CDN: Forward request alt CDN hit CDN-->>SW: Cached response (s-maxage) SW-->>Browser: Response else CDN miss CDN->>Origin: Forward request Origin-->>CDN: Response + Cache-Control CDN-->>SW: Response SW-->>Browser: Response (stored in SW cache) end end ``` Request path through the three cache layers: service worker, CDN, and origin. ## Decision: The golden rule Content-hashed static assets (e.g., `main.abc123.js`) get `Cache-Control: public, max-age=31536000, immutable`. HTML documents get short TTLs or `no-store` — they are the entry point, and a stale one breaks everything after a deploy. ## Key terms - **Cache-Control**: HTTP response header carrying cache directives like `max-age`, `s-maxage`, and `no-store`. - **ETag**: A validator token the server issues; the browser sends it as `If-None-Match` to revalidate without re-downloading the body. - **s-maxage**: CDN-specific TTL that overrides `max-age` for shared caches; the browser ignores it. - **stale-while-revalidate**: Directive (and SW strategy) that serves a stale response instantly, then refreshes in the background. - **immutable**: Tells browsers the resource will never change within `max-age`; suppresses revalidation on hard refresh. ## Related topics - [CDN & Edge Caching](https://fearchitect.com/topics/cdn-edge-caching.md): Serve cached responses from PoPs near users, sparing the origin. - [Network Performance](https://fearchitect.com/topics/network-performance.md): Hint, prioritize, and pre-navigate to cut request latency. - [PWA & Offline](https://fearchitect.com/topics/pwa-offline.md): Installable, offline-capable web apps via service workers and manifests. - [Bundle Architecture & Code Splitting](https://fearchitect.com/topics/bundle-architecture-code-splitting.md): Ship only the JS a route needs, cache the rest long-term. - [REST vs GraphQL vs tRPC](https://fearchitect.com/topics/rest-vs-graphql-vs-trpc.md): Three API styles with distinct fetch, type, and caching trade-offs. - [Edge Computing & Rendering](https://fearchitect.com/topics/edge-computing-rendering.md): Run code at CDN PoPs to cut latency before origin is hit. ## Further reading - [MDN — Cache-Control](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) - [web.dev — HTTP cache](https://web.dev/articles/http-cache) - [web.dev — Service worker caching strategies](https://web.dev/articles/offline-cookbook) - [MDN — ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag)