背景与价值
缓存治理影响性能与安全,需对静态资源与动态接口分别治理,确保私有数据不可缓存且公共资源长效稳定命中。
统一规范
- TTL上限:`max-age`/`s-maxage` 不超过 86400(1天)除非版本化静态资源。
- 不可缓存:用户相关或敏感响应统一 `no-store`。
- 可缓存静态:版本化资源使用长TTL与 `immutable`,并提供 `ETag` 与 `Vary`。
- 统一 `nosniff` 与内容类型校验。
核心实现
ETag与TTL校验
```ts
function b64url(buf: ArrayBuffer): string { const u = new Uint8Array(buf); let s=''; for (let i=0;i {
const d = await crypto.subtle.digest('SHA-256', body)
return 'W/"' + b64url(d) + '"'
}
function ttlValid(ttl: number): boolean { return Number.isInteger(ttl) && ttl >= 0 && ttl <= 86400 }
```
缓存策略分配
```ts
type Req = { path: string; userSpecific?: boolean }
type Res = { setHeader: (k: string, v: string) => void; end: (b?: string) => void }
function isVersioned(path: string): boolean { return /\.[a-f0-9]{8,}\./.test(path) }
function isStatic(path: string): boolean { return /\.(js|css|png|jpg|svg|woff2)$/.test(path) }
async function setCacheHeaders(req: Req, res: Res, body: ArrayBuffer) {
res.setHeader('X-Content-Type-Options', 'nosniff')
res.setHeader('Vary', 'Accept-Encoding')
if (req.userSpecific) {
res.setHeader('Cache-Control', 'private, no-store')
return
}
if (isStatic(req.path) && isVersioned(req.path)) {
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
const t = await etag(body)
res.setHeader('ETag', t)
return
}
const ttl = 60
if (!ttlValid(ttl)) {
res.setHeader('Cache-Control', 'no-store')
return
}
res.setHeader('Cache-Control', `public, max-age=${ttl}, s-maxage=${ttl}`)
const t = await etag(body)
res.setHeader('ETag', t)
}
```
落地建议
- 静态资源启用版本化命名并配置长TTL与 `immutable`,动态接口按短TTL或不缓存。
- 用户态或敏感信息全部使用 `private, no-store`,避免代理与浏览器缓存。
- 严格校验TTL上限并统一生成弱ETag以减少不必要重验证。
- 配置 `Vary: Accept-Encoding` 与 `nosniff`,确保压缩与类型安全。
验证清单
- 版本化静态是否命中长TTL与 `immutable`,ETag是否生成。
- 动态接口是否统一短TTL或不缓存,用户态是否 `no-store`。
- `Vary` 与 `nosniff` 是否统一下发。
发表评论 取消回复