Caching strategies skill

The fastest network request is one that never happens.

by wondelai·MIT license·★ 2,235 Stars on the repo·GitHub ↗

Use now

Files of Caching strategies

wondelai/main1 file
caching-strategies.md
Show the full text355 lines

Caching Strategies

The fastest network request is one that never happens. A well-designed caching strategy eliminates redundant data transfer, reduces server load, and dramatically improves load times for repeat visitors and subsequent navigations.

Table of Contents

  1. The Cache Hierarchy
  2. HTTP Cache-Control Headers
  3. Conditional Requests and Revalidation
  4. Content Hashing for Cache Busting
  5. Service Workers for Cache Control
  6. CDN Configuration
  7. Stale-While-Revalidate
  8. Caching Strategy by Resource Type
  9. Common Caching Mistakes

The Cache Hierarchy

Browsers check caches in a specific order before making a network request:

1. Memory cache (in-process, lost on tab close)
2. Service worker cache (programmable, persistent)
3. Disk cache (HTTP cache, persistent)
4. CDN / edge cache (network, shared across users)
5. Origin server (final fallback)

Each layer closer to the user is faster. Memory cache is near-instant. Disk cache avoids the network entirely. CDN cache reduces RTT by serving from a nearby edge location. The goal is to satisfy as many requests as possible from the closest cache layer.

HTTP Cache-Control Headers

The Cache-Control header is the primary mechanism for controlling browser and CDN caching behavior.

Essential directives
Directive Meaning Use case
max-age=N Cache for N seconds without revalidation Static assets with known freshness
no-cache Cache but always revalidate before use HTML documents, API responses
no-store Do not cache at all Sensitive data (banking, health)
immutable Never revalidate (even on reload) Content-hashed static assets
public Can be cached by shared caches (CDN) Public content
private Only browser can cache, not CDN User-specific content
stale-while-revalidate=N Serve stale for N seconds while fetching fresh Near-real-time content
stale-if-error=N Serve stale if origin returns an error Fault tolerance
Common caching patterns

Static assets with content hashing (optimal):

Cache-Control: max-age=31536000, immutable

Files like app.a1b2c3.js can be cached forever because the URL changes when content changes. immutable tells the browser to skip revalidation even when the user hits refresh.

HTML documents:

Cache-Control: no-cache

The browser caches the document but revalidates on every request. Combined with ETag, this enables 304 Not Modified responses that transfer only headers, not the full document.

API responses with near-real-time needs:

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

Always revalidate, but if the origin is slow, serve the cached response and update in the background.

Sensitive content:

Cache-Control: no-store

Never cache. Use for authentication tokens, financial data, personal health information.

Shared public content:

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

CDN can cache for 1 hour; serve stale for up to 24 hours while refreshing.

Conditional Requests and Revalidation

When a cached resource expires (or uses no-cache), the browser sends a conditional request to check if the resource has changed.

ETag (Entity Tag)

The server generates a unique identifier (hash) for the response content:

HTTP/1.1 200 OK
ETag: "abc123def456"
Cache-Control: no-cache

On revalidation, the browser sends:

GET /page.html HTTP/1.1
If-None-Match: "abc123def456"

If the content has not changed, the server responds:

HTTP/1.1 304 Not Modified

No body is transferred -- only headers. This saves bandwidth while ensuring freshness.

Last-Modified

A simpler mechanism using timestamps:

HTTP/1.1 200 OK
Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT

Revalidation:

GET /page.html HTTP/1.1
If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT

ETag vs. Last-Modified: ETag is more precise (content-based), while Last-Modified has second-level granularity. Servers should support both; browsers prefer ETag when both are present.

Content Hashing for Cache Busting

The most effective caching pattern combines long-lived cache headers with content-hashed filenames:

styles.css → styles.a1b2c3.css
app.js     → app.d4e5f6.js
logo.png   → logo.g7h8i9.png

How it works:

  1. Build tools generate a hash of each file's contents
  2. The hash is embedded in the filename
  3. HTML references the hashed filename
  4. The server sets Cache-Control: max-age=31536000, immutable
  5. When the file changes, a new hash produces a new URL
  6. The browser treats it as a completely new resource

Implementation with common build tools:

Webpack:

output: {
  filename: '[name].[contenthash].js',
  chunkFilename: '[name].[contenthash].js',
}

Vite:

build: {
  rollupOptions: {
    output: {
      entryFileNames: 'assets/[name].[hash].js',
      chunkFileNames: 'assets/[name].[hash].js',
      assetFileNames: 'assets/[name].[hash].[ext]',
    }
  }
}

The HTML document itself cannot be hashed (users navigate to /index.html, not /index.a1b2c3.html). This is why HTML uses no-cache while all referenced assets use content hashing.

Service Workers for Cache Control

Service workers provide a programmable cache layer between the browser and the network. They intercept every fetch request and can implement sophisticated caching strategies.

Cache-first (offline-first)

Serve from cache if available; fall back to network:

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

Best for: Static assets (CSS, JS, images, fonts) that rarely change.

Network-first

Try the network; fall back to cache if offline:

self.addEventListener('fetch', event => {
  event.respondWith(
    fetch(event.request)
      .then(response => {
        const clone = response.clone();
        caches.open('dynamic').then(cache => cache.put(event.request, clone));
        return response;
      })
      .catch(() => caches.match(event.request))
  );
});

Best for: HTML documents and API responses where freshness matters.

Stale-while-revalidate

Serve from cache immediately; update cache in the background:

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cached => {
      const fetchPromise = fetch(event.request).then(response => {
        const clone = response.clone();
        caches.open('dynamic').then(cache => cache.put(event.request, clone));
        return response;
      });
      return cached || fetchPromise;
    })
  );
});

Best for: Content that updates periodically but where instant display is preferred (news feeds, social timelines).

Precaching the app shell

During service worker installation, cache the core application shell:

const CACHE_NAME = 'app-shell-v1';
const SHELL_URLS = [
  '/',
  '/styles.css',
  '/app.js',
  '/offline.html',
];

self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(SHELL_URLS))
  );
});
Cache versioning and cleanup

Old caches must be cleaned up to prevent storage bloat:

self.addEventListener('activate', event => {
  event.waitUntil(
    caches.keys().then(keys =>
      Promise.all(
        keys
          .filter(key => key !== CACHE_NAME)
          .map(key => caches.delete(key))
      )
    )
  );
});

CDN Configuration

CDNs cache content at edge locations close to users, reducing RTT for cached responses.

CDN caching headers

The Cache-Control header controls both browser and CDN caching. Use s-maxage to set a different TTL for shared caches (CDNs) vs. browsers:

Cache-Control: public, max-age=60, s-maxage=3600

This tells browsers to cache for 60 seconds but CDNs to cache for 1 hour.

Vary header

The Vary header tells caches which request headers affect the response:

Vary: Accept-Encoding

Without Vary: Accept-Encoding, a CDN might serve a Brotli-compressed response to a client that only supports Gzip.

Common Vary values:

  • Accept-Encoding -- different compression (Brotli, Gzip, identity)
  • Accept -- different content types (HTML vs. JSON, AVIF vs. WebP)
  • Accept-Language -- different language versions

Warning: Vary: * or Vary: Cookie effectively disables CDN caching because every request differs.

Cache purging

When content changes, CDN caches must be invalidated:

  • Purge by URL: Invalidate a specific resource
  • Purge by tag: Tag resources with categories; purge all resources with a tag
  • Purge by prefix: Invalidate all resources under a path
  • Soft purge: Mark as stale; serve stale while fetching fresh (similar to stale-while-revalidate)

Content-hashed URLs largely eliminate the need for cache purging of static assets. Focus purging on HTML and API responses.

Stale-While-Revalidate

The stale-while-revalidate directive is one of the most powerful caching tools:

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

Behavior:

  1. 0-60 seconds: Serve from cache without revalidation (fresh)
  2. 60-3660 seconds: Serve from cache immediately (stale) AND fetch a fresh copy in the background
  3. After 3660 seconds: Cache is completely stale; must wait for network

This pattern gives users instant responses while keeping content reasonably fresh. It is ideal for:

  • API endpoints that update periodically
  • Configuration data
  • Product listings
  • Any content where a few minutes of staleness is acceptable

Caching Strategy by Resource Type

Resource Cache-Control Hash Revalidation
HTML no-cache No ETag + 304
CSS (bundled) max-age=31536000, immutable Yes None needed
JavaScript (bundled) max-age=31536000, immutable Yes None needed
Images (static) max-age=31536000, immutable Yes None needed
Fonts max-age=31536000, immutable Yes None needed
API responses max-age=0, stale-while-revalidate=60 No ETag + 304
User-specific data private, no-cache No ETag + 304
Sensitive data no-store No N/A

Common Caching Mistakes

Mistake Consequence Fix
No Cache-Control on static assets Browser uses heuristic caching (unpredictable) Explicitly set max-age and immutable
no-store on everything Every visit fetches all resources from origin Use no-cache for HTML; long cache + hash for assets
Missing Vary: Accept-Encoding CDN serves wrong compression format Add Vary: Accept-Encoding to compressed responses
Cache busting with query strings Some CDNs ignore query strings; proxies may not cache Use filename hashing instead of ?v=123
No ETag on HTML Conditional requests impossible; full document re-downloaded Configure server to generate ETags
max-age=0 without stale-while-revalidate Every request blocks on revalidation Add stale-while-revalidate for better perceived performance
Service worker caching everything Cache grows unbounded; stale content persists Implement cache limits and versioned cleanup

A comprehensive caching strategy is often the single highest-impact performance optimization -- it reduces server load, saves bandwidth, and makes repeat visits feel instant.

1# Caching Strategies
2 
3The fastest network request is one that never happens. A well-designed caching strategy eliminates redundant data transfer, reduces server load, and dramatically improves load times for repeat visitors and subsequent navigations.
4 
5 
6## Table of Contents
71. [The Cache Hierarchy](#the-cache-hierarchy)
82. [HTTP Cache-Control Headers](#http-cache-control-headers)
93. [Conditional Requests and Revalidation](#conditional-requests-and-revalidation)
104. [Content Hashing for Cache Busting](#content-hashing-for-cache-busting)
115. [Service Workers for Cache Control](#service-workers-for-cache-control)
126. [CDN Configuration](#cdn-configuration)
137. [Stale-While-Revalidate](#stale-while-revalidate)
148. [Caching Strategy by Resource Type](#caching-strategy-by-resource-type)
159. [Common Caching Mistakes](#common-caching-mistakes)
16 
17---
18 
19## The Cache Hierarchy
20 
21Browsers check caches in a specific order before making a network request:
22 
23```
241. Memory cache (in-process, lost on tab close)
252. Service worker cache (programmable, persistent)
263. Disk cache (HTTP cache, persistent)
274. CDN / edge cache (network, shared across users)
285. Origin server (final fallback)
29```
30 
31Each layer closer to the user is faster. Memory cache is near-instant. Disk cache avoids the network entirely. CDN cache reduces RTT by serving from a nearby edge location. The goal is to satisfy as many requests as possible from the closest cache layer.
32 
33## HTTP Cache-Control Headers
34 
35The `Cache-Control` header is the primary mechanism for controlling browser and CDN caching behavior.
36 
37### Essential directives
38 
39| Directive | Meaning | Use case |
40|-----------|---------|----------|
41| `max-age=N` | Cache for N seconds without revalidation | Static assets with known freshness |
42| `no-cache` | Cache but always revalidate before use | HTML documents, API responses |
43| `no-store` | Do not cache at all | Sensitive data (banking, health) |
44| `immutable` | Never revalidate (even on reload) | Content-hashed static assets |
45| `public` | Can be cached by shared caches (CDN) | Public content |
46| `private` | Only browser can cache, not CDN | User-specific content |
47| `stale-while-revalidate=N` | Serve stale for N seconds while fetching fresh | Near-real-time content |
48| `stale-if-error=N` | Serve stale if origin returns an error | Fault tolerance |
49 
50### Common caching patterns
51 
52**Static assets with content hashing (optimal):**
53```
54Cache-Control: max-age=31536000, immutable
55```
56Files like `app.a1b2c3.js` can be cached forever because the URL changes when content changes. `immutable` tells the browser to skip revalidation even when the user hits refresh.
57 
58**HTML documents:**
59```
60Cache-Control: no-cache
61```
62The browser caches the document but revalidates on every request. Combined with `ETag`, this enables `304 Not Modified` responses that transfer only headers, not the full document.
63 
64**API responses with near-real-time needs:**
65```
66Cache-Control: max-age=0, stale-while-revalidate=60
67```
68Always revalidate, but if the origin is slow, serve the cached response and update in the background.
69 
70**Sensitive content:**
71```
72Cache-Control: no-store
73```
74Never cache. Use for authentication tokens, financial data, personal health information.
75 
76**Shared public content:**
77```
78Cache-Control: public, max-age=3600, stale-while-revalidate=86400
79```
80CDN can cache for 1 hour; serve stale for up to 24 hours while refreshing.
81 
82## Conditional Requests and Revalidation
83 
84When a cached resource expires (or uses `no-cache`), the browser sends a conditional request to check if the resource has changed.
85 
86### ETag (Entity Tag)
87 
88The server generates a unique identifier (hash) for the response content:
89 
90```
91HTTP/1.1 200 OK
92ETag: "abc123def456"
93Cache-Control: no-cache
94```
95 
96On revalidation, the browser sends:
97```
98GET /page.html HTTP/1.1
99If-None-Match: "abc123def456"
100```
101 
102If the content has not changed, the server responds:
103```
104HTTP/1.1 304 Not Modified
105```
106 
107No body is transferred -- only headers. This saves bandwidth while ensuring freshness.
108 
109### Last-Modified
110 
111A simpler mechanism using timestamps:
112 
113```
114HTTP/1.1 200 OK
115Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT
116```
117 
118Revalidation:
119```
120GET /page.html HTTP/1.1
121If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT
122```
123 
124**ETag vs. Last-Modified:** ETag is more precise (content-based), while Last-Modified has second-level granularity. Servers should support both; browsers prefer ETag when both are present.
125 
126## Content Hashing for Cache Busting
127 
128The most effective caching pattern combines long-lived cache headers with content-hashed filenames:
129 
130```
131styles.css → styles.a1b2c3.css
132app.js → app.d4e5f6.js
133logo.png → logo.g7h8i9.png
134```
135 
136**How it works:**
1371. Build tools generate a hash of each file's contents
1382. The hash is embedded in the filename
1393. HTML references the hashed filename
1404. The server sets `Cache-Control: max-age=31536000, immutable`
1415. When the file changes, a new hash produces a new URL
1426. The browser treats it as a completely new resource
143 
144**Implementation with common build tools:**
145 
146Webpack:
147```javascript
148output: {
149 filename: '[name].[contenthash].js',
150 chunkFilename: '[name].[contenthash].js',
151}
152```
153 
154Vite:
155```javascript
156build: {
157 rollupOptions: {
158 output: {
159 entryFileNames: 'assets/[name].[hash].js',
160 chunkFileNames: 'assets/[name].[hash].js',
161 assetFileNames: 'assets/[name].[hash].[ext]',
162 }
163 }
164}
165```
166 
167**The HTML document itself cannot be hashed** (users navigate to `/index.html`, not `/index.a1b2c3.html`). This is why HTML uses `no-cache` while all referenced assets use content hashing.
168 
169## Service Workers for Cache Control
170 
171Service workers provide a programmable cache layer between the browser and the network. They intercept every fetch request and can implement sophisticated caching strategies.
172 
173### Cache-first (offline-first)
174 
175Serve from cache if available; fall back to network:
176 
177```javascript
178self.addEventListener('fetch', event => {
179 event.respondWith(
180 caches.match(event.request)
181 .then(cached => cached || fetch(event.request))
182 );
183});
184```
185 
186**Best for:** Static assets (CSS, JS, images, fonts) that rarely change.
187 
188### Network-first
189 
190Try the network; fall back to cache if offline:
191 
192```javascript
193self.addEventListener('fetch', event => {
194 event.respondWith(
195 fetch(event.request)
196 .then(response => {
197 const clone = response.clone();
198 caches.open('dynamic').then(cache => cache.put(event.request, clone));
199 return response;
200 })
201 .catch(() => caches.match(event.request))
202 );
203});
204```
205 
206**Best for:** HTML documents and API responses where freshness matters.
207 
208### Stale-while-revalidate
209 
210Serve from cache immediately; update cache in the background:
211 
212```javascript
213self.addEventListener('fetch', event => {
214 event.respondWith(
215 caches.match(event.request).then(cached => {
216 const fetchPromise = fetch(event.request).then(response => {
217 const clone = response.clone();
218 caches.open('dynamic').then(cache => cache.put(event.request, clone));
219 return response;
220 });
221 return cached || fetchPromise;
222 })
223 );
224});
225```
226 
227**Best for:** Content that updates periodically but where instant display is preferred (news feeds, social timelines).
228 
229### Precaching the app shell
230 
231During service worker installation, cache the core application shell:
232 
233```javascript
234const CACHE_NAME = 'app-shell-v1';
235const SHELL_URLS = [
236 '/',
237 '/styles.css',
238 '/app.js',
239 '/offline.html',
240];
241 
242self.addEventListener('install', event => {
243 event.waitUntil(
244 caches.open(CACHE_NAME)
245 .then(cache => cache.addAll(SHELL_URLS))
246 );
247});
248```
249 
250### Cache versioning and cleanup
251 
252Old caches must be cleaned up to prevent storage bloat:
253 
254```javascript
255self.addEventListener('activate', event => {
256 event.waitUntil(
257 caches.keys().then(keys =>
258 Promise.all(
259 keys
260 .filter(key => key !== CACHE_NAME)
261 .map(key => caches.delete(key))
262 )
263 )
264 );
265});
266```
267 
268## CDN Configuration
269 
270CDNs cache content at edge locations close to users, reducing RTT for cached responses.
271 
272### CDN caching headers
273 
274The `Cache-Control` header controls both browser and CDN caching. Use `s-maxage` to set a different TTL for shared caches (CDNs) vs. browsers:
275 
276```
277Cache-Control: public, max-age=60, s-maxage=3600
278```
279 
280This tells browsers to cache for 60 seconds but CDNs to cache for 1 hour.
281 
282### Vary header
283 
284The `Vary` header tells caches which request headers affect the response:
285 
286```
287Vary: Accept-Encoding
288```
289 
290Without `Vary: Accept-Encoding`, a CDN might serve a Brotli-compressed response to a client that only supports Gzip.
291 
292Common `Vary` values:
293- `Accept-Encoding` -- different compression (Brotli, Gzip, identity)
294- `Accept` -- different content types (HTML vs. JSON, AVIF vs. WebP)
295- `Accept-Language` -- different language versions
296 
297**Warning:** `Vary: *` or `Vary: Cookie` effectively disables CDN caching because every request differs.
298 
299### Cache purging
300 
301When content changes, CDN caches must be invalidated:
302 
303- **Purge by URL:** Invalidate a specific resource
304- **Purge by tag:** Tag resources with categories; purge all resources with a tag
305- **Purge by prefix:** Invalidate all resources under a path
306- **Soft purge:** Mark as stale; serve stale while fetching fresh (similar to `stale-while-revalidate`)
307 
308Content-hashed URLs largely eliminate the need for cache purging of static assets. Focus purging on HTML and API responses.
309 
310## Stale-While-Revalidate
311 
312The `stale-while-revalidate` directive is one of the most powerful caching tools:
313 
314```
315Cache-Control: max-age=60, stale-while-revalidate=3600
316```
317 
318Behavior:
3191. **0-60 seconds:** Serve from cache without revalidation (fresh)
3202. **60-3660 seconds:** Serve from cache immediately (stale) AND fetch a fresh copy in the background
3213. **After 3660 seconds:** Cache is completely stale; must wait for network
322 
323This pattern gives users instant responses while keeping content reasonably fresh. It is ideal for:
324- API endpoints that update periodically
325- Configuration data
326- Product listings
327- Any content where a few minutes of staleness is acceptable
328 
329## Caching Strategy by Resource Type
330 
331| Resource | Cache-Control | Hash | Revalidation |
332|----------|--------------|------|-------------|
333| HTML | `no-cache` | No | ETag + 304 |
334| CSS (bundled) | `max-age=31536000, immutable` | Yes | None needed |
335| JavaScript (bundled) | `max-age=31536000, immutable` | Yes | None needed |
336| Images (static) | `max-age=31536000, immutable` | Yes | None needed |
337| Fonts | `max-age=31536000, immutable` | Yes | None needed |
338| API responses | `max-age=0, stale-while-revalidate=60` | No | ETag + 304 |
339| User-specific data | `private, no-cache` | No | ETag + 304 |
340| Sensitive data | `no-store` | No | N/A |
341 
342## Common Caching Mistakes
343 
344| Mistake | Consequence | Fix |
345|---------|------------|-----|
346| No `Cache-Control` on static assets | Browser uses heuristic caching (unpredictable) | Explicitly set `max-age` and `immutable` |
347| `no-store` on everything | Every visit fetches all resources from origin | Use `no-cache` for HTML; long cache + hash for assets |
348| Missing `Vary: Accept-Encoding` | CDN serves wrong compression format | Add `Vary: Accept-Encoding` to compressed responses |
349| Cache busting with query strings | Some CDNs ignore query strings; proxies may not cache | Use filename hashing instead of `?v=123` |
350| No ETag on HTML | Conditional requests impossible; full document re-downloaded | Configure server to generate ETags |
351| `max-age=0` without `stale-while-revalidate` | Every request blocks on revalidation | Add `stale-while-revalidate` for better perceived performance |
352| Service worker caching everything | Cache grows unbounded; stale content persists | Implement cache limits and versioned cleanup |
353 
354A comprehensive caching strategy is often the single highest-impact performance optimization -- it reduces server load, saves bandwidth, and makes repeat visits feel instant.
355 

Discussion

Alternatives

Docker MCP gatewayDocker's own CLI plugin: run any server from the Docker MCP Catalog in its own container, behind one connection, with secrets kept out of env vars.Coding · MITTechnical Codebase Discovery & Onboarding PromptA prompt designed to guide a deep technical analysis of a code repository to accelerate developer onboarding. It instructs an AI to analyze the entire codebase and generate a structured Markdown document covering architecture, technology stack, key components, execution and data flows, integrations, testing, security, and build/deployment, serving as a technical reference guide.Coding · CC0-1.0NextflowBuild, run, and debug Nextflow data pipelines and nf-core workflows end to end. Use whenever the user mentions Nextflow, nf-core, .nf files, nextflow.config, DSL2, processes/channels/operators, samplesheets, or wants to run a community pipeline (e.g. nf-core/rnaseq, nf-core/sarek), write or test a module/subworkflow with nf-test, configure executors/containers (Docker, Singularity/Apptainer, Conda, Wave), scale a workflow to HPC/SLURM or cloud (AWS Batch, Google Batch, Azure, Kubernetes), or debug a failed/-resume run. Make sure to use this skill for any reproducible scientific/bioinformatics workflow work even if the user does not say the word "Nextflow", and for authoring nf-core-compliant pipelines, modules, configs, and linting.Science · MITCloud Cost OptimizationOptimize cloud costs across AWS, Azure, GCP, and OCI through resource rightsizing, tagging strategies, reserved instances, and spending analysis. Use when reducing cloud expenses, analyzing infrastructure costs, or implementing cost governance policies.Infrastructure & ops · MIT