Claude Code 实战缓存策略:HTTP、CDN、Service Worker 与 Redis
用 Claude Code 为真实应用设计 HTTP、CDN、Service Worker、Redis 缓存与失效流程。
缓存不是让应用变快的魔法开关。在真实产品里,HTTP 头、CDN、Service Worker、Redis 和进程内内存都可能保存数据,而且保存位置、过期时间和风险完全不同。如果只对 Claude Code 说“加缓存”,它可能让页面更快,却留下旧价格、错误库存、甚至把用户私有信息放进共享缓存。
这篇文章给你一套可以直接交给 Claude Code 的缓存设计思路。Masa 在一个小型 Express 应用和内容站点工作流里验证过:真正有效的不是先上复杂工具,而是先决定“哪一层保存什么、能旧多久、如何删除”。
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)]
先做设计决定
让 Claude Code 改代码前,先回答四个问题:
- 哪些用户可以看到同一份响应?
- 数据最多可以旧多久?
- 更新后要删除哪个 key、URL 或缓存标签?
- 缓存出错时由谁按什么顺序回滚?
带 hash 的 JavaScript、CSS 和图片通常可以缓存很久。账单、个人设置、登录后的 HTML 则不能放进共享缓存。MDN 的 HTTP caching 解释了 private cache 和 shared cache 的区别,这是缓存安全的基本前提。
缓存层对比
| 层 | 适合的数据 | TTL 建议 | 失效方式 | 常见失败 |
|---|---|---|---|---|
| 浏览器 HTTP 缓存 | 图片、CSS、JS、短期公开 API | 1 分钟到 1 年 | 文件名变更、ETag、Cache-Control | API 被长缓存导致页面过旧 |
| CDN 或边缘缓存 | 商品列表、文章 HTML、OGP 图片 | 30 秒到 1 天 | 按 URL、标签或部署 purge | 登录后 HTML 被所有人共享 |
| Service Worker Cache API | 离线页、应用外壳、低频 JSON | 按版本 | 发布时改缓存名 | 旧 worker 继续发旧资源 |
| Redis 或应用缓存 | DB 聚合、外部 API、排行榜 | 10 秒到 1 小时 | key 设计、更新事件、TTL | 在生产 Redis 上跑 KEYS |
| 进程内内存 | 配置、短期 feature flag 副本 | 几秒到几分钟 | 重启或显式 clear | 多实例之间数据不一致 |
建议把这张表写进 CLAUDE.md,这样 Claude Code 新增路由时也有统一判断。项目规则的写法可以参考 CLAUDE.md best practices。
用例1:Express 的 HTTP 缓存头
最先修的往往不是 Redis,而是响应头。Cache-Control 告诉浏览器和 CDN 响应可以保存多久。各个指令可以看 MDN 的 Cache-Control header。
复制为 server.js 后即可运行。
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");
});
这里的关键是把公开数据和私有数据放在不同 URL 下。/api/private/ 使用 no-store,浏览器和 CDN 都不应该保存。公开 API 使用短的 max-age,同时通过 s-maxage 让 CDN 保存稍久一点。
给 Claude Code 的指令要具体:认证后的响应必须使用 no-store,只有公开 API 才能使用 s-maxage。
用例2:Redis getOrSet 辅助函数
Redis 适合降低数据库查询和外部 API 的重复调用。安全的默认模式是 cache-aside:先查 Redis,未命中时从源数据读取,再按 TTL 写入 Redis。
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 };
生产环境不要使用 KEYS products:*。key 增多后它可能阻塞 Redis。更稳妥的做法是保存已知 key 列表、用 Redis Set 记录关联 key,或写基于 SCAN 的维护命令。
用例3:Service Worker Cache API 版本管理
Service Worker 可以在浏览器里拦截请求。配套的 Cache API 适合保存离线页、应用外壳和静态资源。风险是旧 worker 代码:如果缓存名不变,部署后用户仍可能拿到旧 JavaScript。
// 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"));
})
);
});
客户端入口注册:
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker.register("/sw.js");
});
}
每次发布都要改变 CACHE_VERSION,并在 activate 阶段删除旧缓存。把这个要求写进 Claude Code 的任务说明里。
用例4:给 Claude Code 的缓存审计 prompt
Claude Code 的价值不只是写一个 helper,而是能横向检查整个仓库。官方 Claude Code common workflows 推荐用小步调查、编辑、验证的方式推进,缓存改造也适合这样做。
你是 Web 应用的缓存审计负责人。
请检查这个仓库中 HTTP 头、CDN 假设、Service Worker、Redis、进程内内存缓存的使用位置。
请输出:
1. 每一层缓存的数据
2. TTL 和失效条件
3. 个人信息或认证响应进入共享缓存的风险
4. 可能出现 stale data 的具体页面
5. 最小修复方案和验证命令
限制:
- 推测必须标注为推测
- 遵守现有项目风格
- 大重构只提出建议,不要直接执行
这个 prompt 本身也是可复用的上下文缓存。不要每次重新解释缓存规则,把审计清单放进模板或 CLAUDE.md,只传当前差异即可。
真实场景
商品目录或模板商店中,公开商品卡片可以短时间放在 CDN,数据库列表放在 Redis 五分钟。编辑商品后删除商品列表 key,并 purge 对应 CDN URL。
管理后台中,销售额、PV、转化率可以缓存 30 秒到 5 分钟。权限、个人通知、账单数据必须使用 private 或 no-store。
文档和博客中,文章 HTML 可以短时间放在边缘节点,带 hash 的静态资源可以长期缓存,离线外壳由 Service Worker 管理。内容量大的 ClaudeCodeLab 类站点很适合这种分层。
外部 API 中,天气、汇率、SaaS 套餐状态等可以用 Redis 吸收频率限制。但要让 Claude Code 检查 API 条款,因为有些服务不允许缓存。
常见陷阱
最危险的是把登录后的 HTML 缓存在 CDN。只要响应依赖 Cookie,就默认使用 private 或 no-store。如果需要 CDN 速度,把公共外壳和用户专属数据拆开。
第二个问题是缺少 Vary。语言、压缩、设备或授权状态会改变响应时,要么拆 URL,要么发送正确的 Vary。
Redis 还会遇到缓存击穿或雪崩。热门 key 同时过期时,大量请求会一起打到数据库。可以加入随机 TTL、短锁,或用 stale-while-revalidate 思路短暂返回旧值。
Service Worker 会让已删除文件继续存活。必须版本化缓存名,在 activate 删除旧缓存,并准备紧急注销 worker 的流程。
失效 runbook
- 明确影响范围:商品、文章、用户,还是全站。
- 先完成数据库写入,失败时不要删除缓存。
- 使用已知 key、关联 key 集合或
SCAN删除 Redis。 - 按 URL 或标签 purge CDN,全量 purge 作为最后手段。
- 提升 Service Worker 缓存版本,确认旧缓存被删除。
- 用
curl -I、浏览器 DevTools 和 Redis hit rate 验证。 - 事故期间先缩短 TTL 或把敏感路由切到
no-store,再逐步恢复。
把这个 runbook 作为 Claude Code 的完成条件。团队流程还可以结合 Claude Code productivity tips。
ClaudeCodeLab 教材与咨询
如果想把这套策略变成团队可复用流程,可以先看 ClaudeCodeLab 产品和模板,统一 CLAUDE.md、review prompt 和审计 prompt。如果困难点是真实仓库里的 CDN、Redis、权限和 review 规则设计,可以使用 Claude Code 培训与咨询。
官方资料建议收藏:MDN 的 HTTP caching、Cache API、Cache-Control,以及 Anthropic 的 Claude Code common workflows。
实际试用本文内容后,Masa 发现静态资源重复请求减少,公开 API 在 CDN TTL 下更稳定,Redis 的 getOrSet 让多余 DB 读取更容易从日志中看见。Service Worker 也暴露出最重要的教训:不删除旧版本,旧 CSS 会留在用户浏览器里。缓存的核心收益不只是速度,而是清楚写下每个值允许在哪里变旧。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
Claude Code Permission Receipt Pattern:记录权限、证据和回滚方式
Claude Code 权限 receipt:记录允许动作、需要批准的边界、验证命令、回滚说明,以及 Gumroad 和咨询 CTA 检查。
Claude Code/Codex 安全 Agent Harness 实战:权限、验证与回滚
用权限策略、执行计划、验证脚本和回滚日志,为 Claude Code 与 Codex 搭建更安全的 AI Agent 工作流。
Claude Code 子代理实战指南:安全委派并行文章与代码工作
用 Claude Code 子代理安全拆分文章和代码工作:委派规则、提示词模板、失败模式与检查清单。