Advanced (Updated: 6/1/2026)

Claude Code Caching Strategies for Real Apps

A practical Claude Code guide to HTTP cache, CDN, Service Worker Cache API, Redis, and cache invalidation.

Claude Code Caching Strategies for Real Apps

Caching is not a magic switch for making an app fast. In a real product, HTTP headers, the CDN, a service worker, Redis, and process memory all store different things for different lifetimes. If you ask Claude Code to “add caching” without boundaries, the app may become faster while still showing old prices, stale inventory, or even user-specific data to the wrong person.

This guide gives you a practical cache strategy you can hand to Claude Code before it edits code. Masa tested these patterns in a small Express app and a content-site workflow; the biggest improvement came from deciding what each layer owns before adding Redis or a service worker.

flowchart LR
  User[Browser] --> Http[HTTP cache]
  Http --> SW[Service worker Cache API]
  SW --> CDN[CDN or edge cache]
  CDN --> App[Node or app server]
  App --> Redis[Redis or app cache]
  App --> DB[(Database)]

Start With Decisions

Before Claude Code writes implementation, answer four questions:

  1. Which users are allowed to see the same response?
  2. How stale can the data be before it becomes harmful?
  3. Which key, URL, or cache tag is deleted after an update?
  4. Who runs the rollback or purge when the cache is wrong?

Static images and hashed JavaScript bundles can usually live for a year. Billing pages, account settings, and authenticated HTML should not be stored in a shared cache. MDN’s HTTP caching guide explains the important difference between private browser caches and shared caches such as proxies or CDNs.

Layer Comparison

LayerBest forTTL guideInvalidation modelCommon failure
Browser HTTP cacheImages, CSS, JS, short public API responses1 minute to 1 yearFilename changes, ETag, Cache-ControlLong-lived API responses show old screens
CDN or edge cacheProduct lists, article HTML, OGP images30 seconds to 1 dayURL purge, tag purge, deploy purgeAuthenticated HTML is cached for everyone
Service Worker Cache APIOffline page, app shell, rarely changed JSONVersion basedChange cache name on releaseOld worker keeps serving old bundles
Redis or app cacheDB aggregates, external API results, rankings10 seconds to 1 hourKey design, update events, TTLProduction Redis is blocked by KEYS
Process memoryConfig values, short feature flag copiesSeconds to minutesRestart or explicit clearMultiple app instances disagree

Put this table in CLAUDE.md so Claude Code has a stable rulebook when adding a new route. For more project instructions, see CLAUDE.md best practices.

Use Case 1: HTTP Cache in Express

The first cache to fix is often not Redis. It is the response header. Cache-Control tells browsers and CDNs how long a response may be stored. The exact directives are documented in MDN’s Cache-Control header.

Copy this as server.js and run it.

npm install express
node server.js
// server.js
const express = require("express");

const app = express();

function cacheControl(req, res, next) {
  const path = req.path;

  if (path.startsWith("/assets/")) {
    res.set("Cache-Control", "public, max-age=31536000, immutable");
    return next();
  }

  if (path.startsWith("/api/private/")) {
    res.set("Cache-Control", "no-store");
    return next();
  }

  if (path.startsWith("/api/public/")) {
    res.set("Cache-Control", "public, max-age=60, s-maxage=300, stale-while-revalidate=600");
    res.set("Vary", "Accept-Encoding");
    return next();
  }

  res.set("Cache-Control", "no-cache");
  next();
}

app.use(cacheControl);
app.use("/assets", express.static("public/assets"));

app.get("/api/public/products", (_req, res) => {
  res.json({ items: ["book", "template", "consultation"], generatedAt: new Date().toISOString() });
});

app.get("/api/private/me", (_req, res) => {
  res.json({ userId: "demo-user", plan: "team" });
});

app.listen(3000, () => {
  console.log("http://localhost:3000");
});

The important design choice is route separation. /api/private/ gets no-store, so neither the browser nor a CDN should store it. Public API responses get a short browser TTL and a longer CDN TTL through s-maxage.

When you prompt Claude Code, write the rule explicitly: “Authenticated responses must use no-store; only public API routes may use s-maxage.”

Use Case 2: Redis getOrSet Helper

Redis is useful when database queries or external APIs are repeated often. The safe default is the cache-aside pattern: check Redis first, load from the source on a miss, then store the value with a TTL.

npm install redis
// cache.js
const { createClient } = require("redis");

const redis = createClient({
  url: process.env.REDIS_URL || "redis://localhost:6379",
});

let connecting;

async function client() {
  if (redis.isOpen) return redis;
  if (!connecting) connecting = redis.connect();
  await connecting;
  return redis;
}

async function getOrSet(key, ttlSeconds, loader) {
  const r = await client();
  const cached = await r.get(key);

  if (cached !== null) {
    return JSON.parse(cached);
  }

  const fresh = await loader();
  await r.set(key, JSON.stringify(fresh), { EX: ttlSeconds });
  return fresh;
}

async function invalidate(keys) {
  const r = await client();
  if (keys.length > 0) {
    await r.del(keys);
  }
}

module.exports = { getOrSet, invalidate };
// products.js
const { getOrSet, invalidate } = require("./cache");

async function loadProductsFromDb() {
  return [
    { id: "p1", name: "Prompt Templates", price: 500 },
    { id: "p2", name: "Claude Code Consultation", price: 15000 },
  ];
}

async function getPublicProducts() {
  return getOrSet("products:list:v1", 300, loadProductsFromDb);
}

async function updateProduct(productId, patch) {
  console.log("update db", productId, patch);
  await invalidate(["products:list:v1", `products:item:${productId}:v1`]);
}

module.exports = { getPublicProducts, updateProduct };

Avoid KEYS products:* in production. As the keyspace grows, it can block Redis. Prefer known key lists, sets of related keys, or a SCAN based maintenance command.

Use Case 3: Service Worker Cache Versioning

A service worker can intercept browser requests. The related Cache API is useful for offline pages, app shells, and static assets. The danger is stale worker code: if the cache name never changes, an old worker may keep serving old JavaScript after a deploy.

// public/sw.js
const CACHE_VERSION = "claude-code-cache-v2026-06-01";
const STATIC_CACHE = `${CACHE_VERSION}:static`;
const PRECACHE_URLS = ["/", "/offline.html", "/assets/app.css"];

self.addEventListener("install", (event) => {
  event.waitUntil(
    caches
      .open(STATIC_CACHE)
      .then((cache) => cache.addAll(PRECACHE_URLS))
      .then(() => self.skipWaiting())
  );
});

self.addEventListener("activate", (event) => {
  event.waitUntil(
    caches
      .keys()
      .then((names) =>
        Promise.all(
          names
            .filter((name) => !name.startsWith(CACHE_VERSION))
            .map((name) => caches.delete(name))
        )
      )
      .then(() => self.clients.claim())
  );
});

self.addEventListener("fetch", (event) => {
  const request = event.request;
  if (request.method !== "GET") return;

  event.respondWith(
    caches.match(request).then((cached) => {
      if (cached) return cached;

      return fetch(request)
        .then((response) => {
          if (response.ok && new URL(request.url).pathname.startsWith("/assets/")) {
            const copy = response.clone();
            caches.open(STATIC_CACHE).then((cache) => cache.put(request, copy));
          }
          return response;
        })
        .catch(() => caches.match("/offline.html"));
    })
  );
});

Register it from your client entry.

if ("serviceWorker" in navigator) {
  window.addEventListener("load", () => {
    navigator.serviceWorker.register("/sw.js");
  });
}

Tell Claude Code that the cache name must include a release date or build ID, and that old caches must be deleted in activate.

Use Case 4: Claude Code Cache Audit Prompt

Claude Code is especially useful when it audits a whole repository instead of adding a single helper. The official Claude Code common workflows encourage small investigation, edit, and verification loops; that fits cache work well.

You are auditing cache behavior in a web application.
Inspect this repository for HTTP headers, CDN assumptions, service workers, Redis, and process-memory caches.

Return:
1. Data stored by each cache layer
2. TTL and invalidation condition
3. Risk that personal or authenticated responses reach a shared cache
4. Concrete screens where stale data can appear
5. Minimal fix plan and verification commands

Constraints:
- Mark assumptions clearly
- Follow existing project patterns
- Suggest large refactors separately instead of applying them immediately

This prompt acts as reusable context. Instead of re-explaining your cache policy every time, keep the audit checklist in a template or CLAUDE.md and only pass the current diff.

Practical Scenarios

For a product catalog or template store, public product cards can use a CDN for a short time while the database list is cached in Redis for five minutes. After an edit, invalidate the product-list key and purge the affected CDN URL.

For an admin dashboard, revenue and page-view aggregates can be cached for 30 seconds to 5 minutes. Permissions, personal notifications, and billing data should be private or no-store.

For docs and blogs, article HTML can sit briefly at the edge, hashed assets can be long lived, and the offline shell can be owned by the service worker. This is the pattern that fits content-heavy sites like ClaudeCodeLab.

For external APIs, use Redis to absorb rate limits for data such as weather, plans, exchange rates, or status feeds. Ask Claude Code to check the API terms before caching, because some providers restrict storage.

Pitfalls

The most serious mistake is caching authenticated HTML in a CDN. If a response depends on cookies, default to private or no-store. If you need CDN speed, split the page so the shared shell is public and user-specific data is fetched later.

Missing Vary headers are another common cause of wrong responses. If language, compression, device type, or authorization changes the response, either separate the URL or send the right Vary header.

Redis can suffer from a cache stampede when a popular key expires and many requests hit the database at once. Add random TTL jitter, a short lock, or a stale-while-revalidate style fallback.

Service workers can keep deleted files alive. Version the cache, remove old names during activate, and keep an emergency procedure for unregistering the worker if a bad release reaches users.

Invalidation Runbook

  1. Define the blast radius: product, article, user, or whole app.
  2. Finish the database write first; do not delete cache if the write fails.
  3. Delete Redis keys using known keys, related-key sets, or SCAN.
  4. Purge CDN by URL or tag. Full purge is the last resort.
  5. Bump the service worker cache version and confirm old caches disappear.
  6. Verify with curl -I, browser DevTools, and Redis hit-rate logs.
  7. During an incident, shorten TTLs or move sensitive routes to no-store, then restore gradually.

Make this runbook part of Claude Code’s definition of done. For broader workflow discipline, pair it with Claude Code productivity tips.

Training, Templates, and Consultation

If you want this turned into a repeatable team workflow, start with the ClaudeCodeLab product and template library so your CLAUDE.md, review prompts, and audit prompts are consistent. If the hard part is mapping CDN, Redis, permissions, and review rules onto a real repository, use the Claude Code training and consultation page.

For official references, read MDN on HTTP caching, the Cache API, Cache-Control, and Anthropic’s Claude Code common workflows.

After trying the examples in this article, Masa found that static asset requests dropped, public API responses became more predictable with CDN-oriented TTLs, and the Redis getOrSet helper made unnecessary database reads visible in logs. The service worker also exposed the main lesson: without versioned deletion, stale CSS survives a deploy. The practical win is not just speed; it is writing down where each cached value is allowed to become old.

#Claude Code #caching #Redis #CDN #Service Worker
Free

Free PDF: Claude Code Cheatsheet

Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.

We handle your data with care and never send spam.

Level up your Claude Code workflow

Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.

Masa

About the Author

Masa

Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.