Skip to main content
Technology & EngineeringPerformance Optimization114 lines

Caching Strategies

Design effective browser and CDN caching strategies using Cache-Control headers, ETags, service workers, and edge caching.

Quick Summary34 lines
You are an expert in browser and CDN caching strategies for optimizing application performance.

## Key Points

- **Cache-Control header:** The primary mechanism for controlling caching behavior. Key directives: `max-age`, `s-maxage`, `no-cache`, `no-store`, `immutable`, `stale-while-revalidate`.
- **ETag / Last-Modified:** Conditional request headers that let the server respond with `304 Not Modified` when content has not changed, saving bandwidth.
- **Content hashing:** Appending a hash to filenames (e.g., `app.3f2a1b.js`) enables aggressive long-term caching because the URL changes whenever the content changes.
- **CDN edge caching:** Serving cached responses from geographically close edge nodes, reducing round-trip latency from hundreds of milliseconds to single digits.
- **Cache-hit ratio:** Monitor CDN analytics for cache-hit rates. Target > 90% for static assets.
- **DevTools Network panel:** Check the "Size" column — `(disk cache)` or `(memory cache)` indicates a cache hit.
- **Server load:** Compare origin request volume before and after caching changes. A significant drop confirms effectiveness.
- **`Vary` header audit:** Ensure `Vary` is not set to overly broad values (like `Vary: *`) which effectively disable caching.
- Use content-hashed filenames for JS, CSS, fonts, and images so you can cache them with `max-age=31536000, immutable`.
- Always set `Vary: Accept-Encoding` when serving compressed responses to prevent CDNs from mixing gzip and Brotli cached copies.
- Use `stale-while-revalidate` for API responses where slightly stale data is acceptable, giving users instant responses while refreshing in the background.
- Setting long `max-age` on HTML files without a cache-busting mechanism, making it impossible to push updates to users until the cache expires.

## Quick Example

```nginx
# Hashed assets — cache for 1 year, immutable
location ~* \.[a-f0-9]{8}\.(js|css|woff2|png|webp|avif)$ {
    add_header Cache-Control "public, max-age=31536000, immutable";
}
```

```nginx
location ~* \.html$ {
    add_header Cache-Control "no-cache";
    # no-cache means the browser MUST revalidate with the server before using the cached copy
}
```
skilldb get performance-optimization-skills/Caching StrategiesFull skill: 114 lines
Paste into your CLAUDE.md or agent config

Caching Strategies — Performance Optimization

You are an expert in browser and CDN caching strategies for optimizing application performance.

Core Philosophy

Overview

Caching eliminates redundant network requests by storing responses closer to the user — in the browser, a service worker, or a CDN edge node. A well-designed caching strategy can reduce server load by 60-90% and cut page load times for repeat visitors to under one second.

Core Concepts

  • Cache-Control header: The primary mechanism for controlling caching behavior. Key directives: max-age, s-maxage, no-cache, no-store, immutable, stale-while-revalidate.
  • ETag / Last-Modified: Conditional request headers that let the server respond with 304 Not Modified when content has not changed, saving bandwidth.
  • Content hashing: Appending a hash to filenames (e.g., app.3f2a1b.js) enables aggressive long-term caching because the URL changes whenever the content changes.
  • CDN edge caching: Serving cached responses from geographically close edge nodes, reducing round-trip latency from hundreds of milliseconds to single digits.

Implementation Patterns

Static Assets with Content Hashing

# Hashed assets — cache for 1 year, immutable
location ~* \.[a-f0-9]{8}\.(js|css|woff2|png|webp|avif)$ {
    add_header Cache-Control "public, max-age=31536000, immutable";
}

HTML Documents — Always Revalidate

location ~* \.html$ {
    add_header Cache-Control "no-cache";
    # no-cache means the browser MUST revalidate with the server before using the cached copy
}

API Responses with Stale-While-Revalidate

Before:

// No caching — every request hits the origin
res.setHeader('Cache-Control', 'no-store');

After:

// Serve cached for 60s, allow stale for 5 min while revalidating in background
res.setHeader('Cache-Control', 'public, max-age=60, stale-while-revalidate=300');
res.setHeader('Vary', 'Accept-Encoding, Authorization');

Service Worker Cache Strategy

// Cache-first for static assets, network-first for API calls
self.addEventListener('fetch', (event) => {
  const { request } = event;

  if (request.url.includes('/api/')) {
    // Network first — fresh data preferred
    event.respondWith(
      fetch(request)
        .then(res => {
          const clone = res.clone();
          caches.open('api-v1').then(c => c.put(request, clone));
          return res;
        })
        .catch(() => caches.match(request))
    );
  } else {
    // Cache first — fast load for static resources
    event.respondWith(
      caches.match(request).then(cached => cached || fetch(request))
    );
  }
});

Measurement & Monitoring

  • Cache-hit ratio: Monitor CDN analytics for cache-hit rates. Target > 90% for static assets.
  • DevTools Network panel: Check the "Size" column — (disk cache) or (memory cache) indicates a cache hit.
  • Server load: Compare origin request volume before and after caching changes. A significant drop confirms effectiveness.
  • Vary header audit: Ensure Vary is not set to overly broad values (like Vary: *) which effectively disable caching.

Best Practices

  • Use content-hashed filenames for JS, CSS, fonts, and images so you can cache them with max-age=31536000, immutable.
  • Always set Vary: Accept-Encoding when serving compressed responses to prevent CDNs from mixing gzip and Brotli cached copies.
  • Use stale-while-revalidate for API responses where slightly stale data is acceptable, giving users instant responses while refreshing in the background.

Common Pitfalls

  • Setting long max-age on HTML files without a cache-busting mechanism, making it impossible to push updates to users until the cache expires.
  • Forgetting to invalidate CDN cache after deployments, causing users to load new HTML that references old, now-deleted asset hashes (resulting in broken pages).

Anti-Patterns

Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.

Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.

Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.

Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.

Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.

Install this skill directly: skilldb add performance-optimization-skills

Get CLI access →