正文ETag(实体标签)是 HTTP 条件请求的核心。强 ETag 适用于字节级一致性判断,弱 ETag 适用于语义一致但字节可能不同的场景(例如:相同数据经不同格式化)。本文给出强/弱 ETag 的生成与 `If-None-Match`、`If-Modified-Since` 的协同治理实践。
一、强/弱 ETag 生成方法// app/api/etag/route.ts
export const runtime = 'edge'
async function strongETag(input: ArrayBuffer) {
const digest = await crypto.subtle.digest('SHA-256', input)
const bytes = new Uint8Array(digest)
let b64 = ''
for (let i = 0; i < bytes.length; i++) b64 += String.fromCharCode(bytes[i])
return '"sha256-' + btoa(b64) + '"'
}
function weakETag(size: number, mtimeMs: number) {
return `W/"${size}-${mtimeMs}"`
}
二、条件请求与 304 响应治理优先使用 ETag 校验,其次使用 `Last-Modified` 与 `If-Modified-Since`。始终在 304 响应中回显当前的标准化响应头(ETag/Last-Modified/Cache-Control),确保中间缓存层的一致性。export async function GET(req: Request) {
// 示例资源:真实场景可来自存储/渲染结果
const body = new TextEncoder().encode('Hello ETag v2')
const lastModified = new Date('2025-11-01T00:00:00Z')
const strong = await strongETag(body.buffer)
const weak = weakETag(body.byteLength, lastModified.getTime())
const inm = req.headers.get('if-none-match') || ''
const ims = req.headers.get('if-modified-since')
// 优先 ETag 校验;兼容强/弱标签
if (inm && (inm.includes(strong) || inm.includes(weak))) {
return new Response(null, {
status: 304,
headers: {
ETag: strong,
'Last-Modified': lastModified.toUTCString(),
'Cache-Control': 'public, max-age=60, s-maxage=300, must-revalidate'
}
})
}
// 次选 Last-Modified 校验
if (ims) {
const imsDate = new Date(ims)
if (!Number.isNaN(imsDate.getTime()) && imsDate.getTime() >= lastModified.getTime()) {
return new Response(null, {
status: 304,
headers: {
ETag: strong,
'Last-Modified': lastModified.toUTCString(),
'Cache-Control': 'public, max-age=60, s-maxage=300, must-revalidate'
}
})
}
}
// 命中失败:返回资源与标准缓存头
return new Response(body, {
headers: {
'Content-Type': 'text/plain; charset=utf-8',
ETag: strong,
'Last-Modified': lastModified.toUTCString(),
'Cache-Control': 'public, max-age=60, s-maxage=300, stale-while-revalidate=600',
'Vary': 'Accept-Encoding'
}
})
}
三、选型建议与注意事项强 ETag:适合静态资源、经过完整哈希的产物(如构建输出),确保字节级一致。弱 ETag:适合语义一致但字节可能不同的场景(JSON 序列化差异、空白字符变化),提高命中率但不用于 Range 请求合并。回显策略:在 304 响应中始终回显当前 ETag 与 Last-Modified,避免中间层状态偏差。`Vary` 管理:仅在必要的协商维度(如 `Accept-Encoding`、`Accept`、UA-CH)设置 `Vary`,防止缓存分片过度导致命中率下降。安全与一致性:为动态生成内容设置合理的 `max-age/s-maxage` 与 `must-revalidate`/`stale-while-revalidate`,避免过时内容长尾污染。
四、扩展:HEAD 与弱标签策略对于目录索引或列表页,可提供 `HEAD` 路由快速探测并返回 ETag/Cache-Control,不携带响应体,降低探测带宽与耗时。弱标签可用于允许“轻微变更但视为未变”场景,需结合业务语义评估适配度。

发表评论 取消回复