From harness-claude
Optimizes HTTP browser caching with Cache-Control directives, ETags, Last-Modified validation, content-hashed immutable assets, and stale-while-revalidate for repeat-visit performance. Use for slow repeat loads, Lighthouse cache flags, or low cache hits.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Master HTTP browser caching — Cache-Control directives, ETag and Last-Modified validation, immutable assets with content-hashed filenames, stale-while-revalidate patterns, and cache partitioning in modern browsers for optimal repeat-visit performance.
Guides HTTP caching with Cache-Control directives, ETag/Last-Modified validation, and Vary headers. Useful for API endpoints, CDN debugging, cache invalidation, and PR reviews.
Guides Netlify CDN caching: Cache-Control headers, stale-while-revalidate, cache tags, on-demand purges, durable cache, vary keys, and Next.js ISR patterns.
Guides designing CDN architectures, caching strategies, and global content distribution. Covers cache hierarchies, origin shielding, invalidation, and edge optimization.
Share bugs, ideas, or general feedback.
Master HTTP browser caching — Cache-Control directives, ETag and Last-Modified validation, immutable assets with content-hashed filenames, stale-while-revalidate patterns, and cache partitioning in modern browsers for optimal repeat-visit performance.
Audit current caching behavior. In Chrome DevTools Network panel, check the "Size" column. Values like "(disk cache)" or "(memory cache)" indicate cached resources. Look at response headers for Cache-Control directives. Resources without caching headers or with no-store are fetched fresh every time.
Design a caching strategy by content type. Different content types need different caching policies:
Static assets (JS, CSS, images) with content hash:
Cache-Control: public, max-age=31536000, immutable
HTML documents:
Cache-Control: no-cache
(or: Cache-Control: public, max-age=0, must-revalidate)
API responses (cacheable):
Cache-Control: private, max-age=60, stale-while-revalidate=300
User-specific data:
Cache-Control: private, no-cache
Sensitive data (banking, health):
Cache-Control: no-store
Implement content-hashed filenames. Content hashing ensures cached files are automatically invalidated when content changes:
# Build output with content hash
app.a1b2c3d4.js ← hash changes when code changes
style.e5f6g7h8.css ← hash changes when styles change
vendor.i9j0k1l2.js ← hash rarely changes (stable dependencies)
# Cache-Control for hashed files
Cache-Control: public, max-age=31536000, immutable
# 1 year TTL + immutable = browser never revalidates
The HTML document (not content-hashed) references specific hashed filenames. When you deploy, the HTML changes to reference new hashes, and the browser fetches the new files.
Configure ETag validation for dynamic content. ETags allow the browser to check if content has changed without re-downloading:
# First request
GET /api/products
→ 200 OK
→ ETag: "abc123"
→ Cache-Control: no-cache
# Subsequent request (browser sends conditional request)
GET /api/products
If-None-Match: "abc123"
→ 304 Not Modified (no body, saves bandwidth)
Use stale-while-revalidate for balanced freshness. This directive serves the cached version immediately while fetching a fresh copy in the background:
Cache-Control: public, max-age=3600, stale-while-revalidate=86400
Timeline:
0-1h: Serve from cache (fresh)
1h-25h: Serve from cache (stale) + revalidate in background
>25h: Must revalidate before serving (cache expired)
Understand browser cache layers. Browsers maintain multiple cache tiers:
Account for cache partitioning. Modern browsers (Chrome 86+, Firefox 85+) partition the HTTP cache by top-level site. A resource cached when visiting site-a.com is NOT reused when visiting site-b.com, even if the URL is identical. This impacts shared CDN-hosted resources (Google Fonts, cdnjs, unpkg) — self-hosting eliminates the cross-site cache miss.
| Directive | Meaning |
|---|---|
public | Any cache (browser, CDN, proxy) may store this response |
private | Only the end-user browser may cache (not CDN or proxy) |
max-age=N | Cache is fresh for N seconds from response time |
s-maxage=N | CDN/proxy TTL (overrides max-age for shared caches) |
no-cache | Cache the response but revalidate before every use |
no-store | Do not cache at all — not in memory, not on disk |
must-revalidate | After max-age expires, MUST revalidate (no stale serving) |
immutable | Content will never change — skip revalidation entirely |
stale-while-revalidate=N | Serve stale for N seconds while revalidating in background |
stale-if-error=N | Serve stale for N seconds if origin returns an error |
Twitter serves static assets with Cache-Control: public, max-age=31536000, immutable using content-hashed filenames (e.g., main.a1b2c3d4.js). This eliminates all revalidation requests on repeat visits — the browser serves directly from disk cache without any network activity. On deploy, the HTML document (cached with no-cache) references new hashed filenames, triggering fresh downloads only for changed assets. Unchanged assets (vendor libraries, shared components) remain cached. This approach achieves >99% cache hit rate for static assets on repeat visits.
The Financial Times implemented stale-while-revalidate with a 1-hour fresh window and 24-hour stale window for their article pages. Result: 95% of page views are served instantly from cache (either fresh or stale), with background revalidation keeping content within 1 hour of the latest version. For breaking news, they supplement with explicit cache purging via the CDN API. This approach reduced perceived page load time by 70% on repeat visits compared to their previous no-cache policy.
Using no-cache when you mean no-store. no-cache DOES cache the response — it just requires revalidation before each use (a conditional request). no-store prevents caching entirely. For truly sensitive data (banking details, health records), use no-store. For HTML that should always be fresh, use no-cache (allows 304 responses).
Setting short max-age on versioned assets. If filenames contain content hashes, the content at that URL will never change. Setting max-age=3600 on app.a1b2c3.js forces unnecessary revalidation every hour. Use max-age=31536000, immutable instead.
Forgetting Vary header with content negotiation. If your server returns different content based on Accept-Encoding or Accept-Language, the Vary header must list those headers. Without it, a cache may serve a gzip-compressed response to a client that sent Accept-Encoding: br.
Query string cache busting instead of filename hashing. URLs like /app.js?v=123 rely on all caches respecting query parameters. Some CDNs strip or ignore query strings by default. Content-hashed filenames (/app.abc123.js) are universally reliable.
max-age=31536000, immutable caching.no-cache or equivalent to ensure fresh content on navigation.