按比例展示 30 秒新鲜期与随后 60 秒 stale-if-error 许可。历史离线模型在年龄 35 先验证,遇到符合条件的错误才保留 A;年龄 91 返回错误。失败不会重置 Age。

陈旧缓存需要有界策略

陈旧响应可以在源站故障时维持公开手册的可读性,也可能保留过时价格、已撤销权限或误导性的状态。可用性并不天然高于正确性。启用这种行为之前,应先确定哪些信息允许继续使用、什么条件下可以复用,以及最多能旧多久,而不是看到一次错误响应就无条件拿旧成功结果代替。 历史 Node/http-cache-semantics 实验用虚构响应核对有界策略与调用方所选正文。年龄 35、89 可覆盖符合条件的错误,91 不行;失败不会重新开启窗口。离线通过不等于真实网络或故障测试,本图没有新增执行。 用可用性换新鲜度 过期和淘汰是两件事。过期响应仍可留在缓存里,用于条件验证或受到许可的陈旧响应;但冷缓存或已经淘汰条目的缓存,没有旧正文可返回。陈旧指令无法凭空创造副本,保存着字节也不会自动授予复用权限。选中的响应还必须匹配当前请求,包括相关 Vary 维度和访问边界,不能拿另一个请求留下的成功内容填补故障。 如何表示 stale-while-revalidate 允许在有界窗口内返回旧响应,同时尝试不阻塞该响应的验证。stale-if-error 则允许在尝试访问源站遭遇符合条件的错误时使用旧响应。两者触发条件不同,都不强制缓存保存内容,也不能保证每种缓存实现都支持。请求到达时若已越过允许窗口,不能因为之前有过一次许可,就继续无限期使用旧内容。 对于 max-age=30, stale-if-error=60,额外六十秒从新鲜期结束后开始计算,不是从每一次失败尝试重新开始。候选响应的总年龄窗口约为九十秒。RFC 5861 指定了会形成 500、502、503、504 的错误场景。认证失败或资源不存在,不能随手重新包装成可用性故障,再用旧的成功响应掩盖真正需要客户端看到的结果。 成功的条件验证不一定重新下载正文。三零四响应可以依据验证元数据更新存储响应,同时保留原来的表示字节。应把这种结果与替换正文的二百响应、故障期间的陈旧命中分别记录;三种情况可能显示相同页面,却对应不同的新鲜度依据,不能仅靠正文是否改变判断验证是否发生。 坏掉的样子 每次重验证失败后都把 Age 清零,会把有界例外变成无限陈旧。保留旧正文却刷新存储时间戳,也会造成同类错误。失败时应保留年龄;只有真正的新响应或成功验证,才提供更新新鲜度的依据。后台验证通常由流量触发,所以一个长期无人访问的缓存,不一定会在下一次用户访问之前主动把内容更新好。 只读或幂等并不代表可以安全返回旧内容。一个 GET 也可能涉及对时效敏感的权限或价格。RFC 9111 禁止在适用的 no-cache、must-revalidate 等指令不允许时复用陈旧响应。应避免互相矛盾的策略,并测试实际部署的缓存如何处理这些限制。另外,RFC 5861 中关于旧 Warning 字段的建议,已被废弃该字段的 RFC 9111 更新,不能把它当作当前验收必需项。 ...

二月 1, 2027 · 2 分钟 · XB Field Notes
旧红色字节保留旧摘要文件名,变化后的蓝色字节使用新摘要文件名。

不可变资源与内容指纹

资源指纹把文件字节的摘要放进地址。样式表发生变化,构建就为新内容生成另一个地址,页面因此能够选择新版本,而不用等待所有旧缓存同时过期。这项方法依赖发布流程遵守承诺:某个地址一旦对外提供,就不能随意替换成与名称摘要不符的内容。文件名看起来像摘要,不代表服务器已经落实这种约束。 字节变化应使用新的摘要地址;先让资源可用,再发布引用它们的 HTML,并保留仍被引用的旧文件。覆盖旧地址会让新旧客户端看到不同内容。文件名摘要不是浏览器完整性检查;本图未重新执行实验。 名称配合新鲜度契约 构建程序可以对生成文件计算完整的 SHA-256,再把页面中的逻辑名称替换成相应地址。同样的字节保留相同名称,变化的字节通常得到不同名称;摘要截断越短,碰撞风险越需要认真评估。浏览器不会因为名称含有一串十六进制字符,就自动计算文件摘要验证完整性。实际缓存键还受到请求上下文和 Vary 等因素影响,命名方法不能取代 HTTP 缓存规则。 公开指纹资源常用的响应头是 Cache-Control: public, max-age=31536000, immutable。前者给出一年新鲜期,后者承诺该表示在新鲜期内不会改变,因此 RFC 8246 建议客户端避免不必要的条件请求。这不是永久保存许可:缓存可能被淘汰,用户可以强制刷新,客户端也可能不支持扩展。规范还建议在 HTTPS 等经过认证的上下文之外忽略 immutable,不能把它当作内容可信性的替代品。 负责引用这些资源的 HTML 需要独立缓存策略,例如较短新鲜期或合适的重新验证,让用户能够发现新版本。发布顺序应先让新资源可用,再暴露引用它们的页面或清单。旧页面、旧标签页和回退版本仍可能使用旧地址,资源保留期限必须覆盖这些真实使用方式。仅有指纹,不能让整个多文件发布自动成为原子事务。 public 只适合允许共享的表示。某份个性化下载即使带有内容摘要,也不会因此变成可供共享缓存保存的公开资源。响应策略仍然必须服从身份鉴别与保密要求;摘要只是命名依据,不能充当授权或隔离用户数据的措施。 坏掉的样子 覆盖旧指纹路径会制造两套互相矛盾的事实:已有用户继续从缓存读取红色样式,新客户端却在同一个地址读到蓝色字节。清理一个 CDN 节点,也不能把所有浏览器里的私有副本同步删除。发布检查应核对真实文件字节和记录的摘要,在开放流量之前拒绝这种变更,而不是依赖上线后提醒用户刷新。 立即删除上一版资源会产生另一类故障。缓存中的页面还可能引用昨日脚本,回退后的 HTML 也可能依赖已经移除的文件。保留源码提交不等于保留可直接恢复的构建产物。即使所有摘要都正确,先发布页面、后上传文件仍会造成暂时的缺失资源;多节点传播期间也需要检查依赖是否已经就绪。 ...

十一月 25, 2026 · 2 分钟 · XB Field Notes
接收时的两种估计为 12 秒与 7 秒,取较大值 12,再加 8 秒本地驻留,得到当前年龄 20 秒。

Age 头告诉我们缓存已存在多久

Age 是从源站生成或成功验证响应以来,已经经过多少秒的估计值,包含缓存驻留和网络传输时间。它不是对象的创建时间戳,也不是访问次数,更不只是最后一台代理在磁盘上保存副本的时长。要判断这个数字意味着什么,必须同时查看响应的新鲜度策略和实际请求上下文,不能把年龄直接等同于是否允许复用。 先取接收时两种估计的较大值,再加本地驻留时间。新鲜度是严格的年龄比较,并不授权复用。 合并估计时不能重复计时 RFC 9111 定义了接收响应时的两种年龄估计。表观年龄用接收缓存的时钟减去 Date,如果得到负数,就按零处理。校正年龄从收到的 Age 数值出发,再加上发出请求到收到响应之间的时间。缺少 Age 时,这个输入按零参与计算,但并不意味着应当丢掉基于 Date 的另一种估计。 ...

十一月 6, 2026 · 2 分钟 · XB Field Notes
文章的新鲜响应样例均带 max-age=300。矩阵每格表示存储和直接复用:private 在私有缓存为是/是、共享缓存为否/否;public 都为是/是;no-cache 都为是/否;no-store 都为否/否。同一浏览器切换账户仍可能匹配旧的私有条目。

Cache-Control:private 还是 shared

存储范围不是身份系统。Cache-Control: private 要求遵守协议的共享缓存不存储这条响应,却不会教会浏览器识别正文属于哪个应用账户。当一个人退出登录,另一个账户在同一浏览器中登录,然后访问相同网址时,这个区别就会变成实际的数据隔离问题,不能只凭响应头里出现 private 就判定安全。 文章的新鲜响应样例将存储资格与无需验证即可复用分开。另一条账户边界说明,私有缓存仍可能匹配旧账户响应。请求字段匹配不能证明当前授权,本图不新增运行时证据。 有资格被缓存不代表能共享 不带字段名参数的 private 允许单用户缓存保存响应,但仍受其他缓存条件约束。浏览器是常见实现,并不是唯一实现。public 则明确允许某些原本可能受到限制的响应进入缓存,例如满足条件的带授权请求响应。已经符合缓存条件的普通响应不一定需要 public;出现 public 也不意味着所有缓存都必须保存它,存储许可和实际是否存储是不同事实。 不带参数的 no-cache 与 no-store 回答的是另一个问题。前者允许存储,但每次复用前必须成功验证;后者禁止私有和共享 HTTP 缓存存储及随后复用该响应。它们都不是加密、访问控制检查,也不承诺清除应用日志或用户之前保存的副本。本文讨论不带字段名参数的形式,不把实现支持较弱的按字段限定形式混进实验。 新鲜度还需要单独判断。错误的个人响应如果按共享键保存,即使只给五秒钟有效期,也可能有五秒钟把数据交给另一个人。缩短 max-age 不能修复身份边界。反过来,s-maxage 只是控制共享缓存的新鲜度,并不是 private 的另一种写法。先决定响应可在哪一层保存,再讨论可直接复用多久,才能避免用性能参数代替安全条件。 ...

十月 23, 2026 · 2 分钟 · XB Field Notes
无别名直接查询中,NXDOMAIN 表示名称缺失,NODATA 表示请求类型缺失。权威负向寿命取 SOA TTL 与 MINIMUM 中较小值。

负向 DNS 回答会把不存在的数据记到缓存寿命结束

DNS 可以通过说明数据缺失提供有效信息。NXDOMAIN 表示名称错误,NODATA 则描述成功响应中没有所请求类型的数据。对于没有别名的直接查询,两者区分名称不存在,以及名称存在但缺少该类数据。缓存这些结果,可以避免每次客户端请求都重新进行相同的无结果上游查询。负向回答也是有内容、有寿命的状态,并不是“没有收到任何东西”。 先分类回答,再解释缓存。来源中的 SOA TTL/MINIMUM 组合 8/4 和 2/9 分别产生四秒和两秒寿命。负向条目可能在权威发布后继续存在。SERVFAIL、超时与验证失败使用另一类失败缓存规则。历史解析器示例未重跑。 坏掉的样子 部署已经创建正确记录,先前缓存过缺失结果的客户端却可能仍然失败。权威返回新数据,缓存则在寿命结束前继续返回旧否定结果。反复删除重建记录,不会使远端缓存自动失效。这种情况常出现在应用提前查询尚未配置完成的服务名称时;排障应对照查询发生时间和缓存寿命,而不是只检查现在的区域文件是否包含记录。 仅凭回答区为空,不能完整分类 DNS 结果。需要检查响应码、权威区和可能存在的别名链。转介与权威否定回答不同,NXDOMAIN 也可能指向 CNAME 最终目标,而非最初请求的别名本身。把所有空结果都说成“域名不存在”,可能让调查去改错误的记录。应保留原始问答的类型、名称及各区数据,避免过早简化成一句错误描述。 对于 RFC 2308 规定的权威 NXDOMAIN 和 NODATA,负向寿命取 SOA.MINIMUM 与 SOA 记录 TTL 中的较小值。该值放在否定回答的 SOA TTL 上,再由缓存继续递减。MINIMUM 不是所有正向记录 TTL 的统一下限;只改变将来正向记录的 TTL,也不会缩短已经缓存的不存在结论。操作时应确认正在调整哪类数据的哪一代寿命。 ...

十月 22, 2026 · 3 分钟 · XB Field Notes
同一 URI 下,两条通道通过 Vary: Accept 将对应的 Accept 偏好与 Content-Type 媒体类型相连。

Content-Type 与 Vary 要一起工作

同一个 URL 可以向 API 客户端返回 JSON,也可以向另一个读者返回纯文本。Content-Type 说明这次选中了什么表示,Vary 则说明哪些请求字段参与了选择。如果缓存不知道应该怎样匹配后续请求,即使正文的类型标签完全正确,也可能把属于另一种偏好的响应送给当前读者。 Content-Type 描述选中的正文,Vary 约束请求匹配。文章所述 CacheControl 示例中,漏写 Vary 会让纯文本请求收到缓存的 JSON。本图不增加实验执行或生产缓存验证结论。 分清格式、选择与复用 如果服务器根据请求的 Accept 字段进行协商,就应在实际响应类型之外返回 Vary: Accept。写成 Vary: Content-Type 指定的是请求里的类型字段,并不是响应的媒体类型;它通常不能表达客户端希望收到哪一种格式。缓存无法仅通过查看已存响应的类型,倒推出服务器选择该表示时使用的规则。目标 URI 与相关方法语义本来就参与普通缓存匹配,Vary 额外约束的是请求头字段。 压缩还涉及另一组容易混淆的概念。经过 gzip 的 JSON 仍然是 JSON 媒体类型,Content-Encoding: gzip 描述对表示施加的内容编码,不是传输编码或消息分帧。如果源站依据 Accept-Encoding 选择是否压缩,这个请求字段就是有关的 Vary 维度。只有真正参与选择的维度才需要纳入;浏览器的实际视口宽度不会自动变成一个请求头,也不能仅凭“这是移动端页面”就推导出缓存键应该包含什么。 ...

十月 12, 2026 · 2 分钟 · XB Field Notes
强验证器区分字节变化;弱验证器可以表达语义等价。错误的固定标签会在对象改变后仍返回错误的 304。

ETag 与条件请求避免多余工作

只有旧表示确实仍可替代当前表示,304 才有价值。ETag 提供的是源站对版本的声明,不会独立检查这个声明是否诚实。错误的标签会让条件请求高效地保留错误内容。因此在把传输减少当作性能改善之前,既要测试未变化的数据,也要测试有意义的内容变更,不能只追求更多的 304。 强验证器区分字节变化;弱验证器可以表达语义等价。错误的固定标签会在对象改变后仍返回错误的 304。 仅为来源示意;本任务未执行示例。 校验器标识版本 实体标签是带引号的不透明标识。客户端应按收到的形式带回它,不应擅自当作时间戳解释,也不应移除弱标签标记。生成器可以使用版本编号或抗碰撞摘要。当最终字节已经存在时,摘要比较方便;如果每次为了返回 304,仍要先完整渲染并计算一份昂贵文档的哈希,源站的计算成本可能并没有明显下降。省掉正文传输不等于省掉所有工作。 强与弱是不同的承诺 所选表示的数据变化时,强验证器必须变化。带缩进的 JSON 与紧凑 JSON 即使解析成同一个对象,字节也不同;gzip 与未编码形式同样具有不同表示数据,强标签必须保留这种区别。长度相同,或者底层数据库的粗粒度时间戳相同,都不能证明逐字节一致。因此验证器的设计要贴近真正返回的表示,而不是只观察某个间接指标。 带 W/ 的弱标签可以把源站认为可互换的表示归为一组。若 JSON 接口契约不关心排版,一个语义版本可以同时覆盖紧凑和缩进序列化。实际字段值变化后,如果旧对象已不再是可接受的替代品,就必须更新该版本。弱验证表达的是明确的等价策略,不是任意变化都允许沿用标签,更不是可以永远不维护版本信息。 If-None-Match 使用弱比较完成缓存验证。条件 GET 匹配时返回不带正文的 304,不匹配时通常返回当前表示与 200。If-Match 则使用强比较,这关系到更新前置条件;客户端也不能用弱标签作为 If-Range 的验证器。这几个字段的契约不同,先去掉 W/ 再统一做字符串比较,会把本来不同的语义混成一件事。 ...

九月 26, 2026 · 2 分钟 · XB Field Notes
在 max-age=60 下,年龄 59 秒仍新鲜,直接复用响应;60 与 61 秒已陈旧,进入验证器比较,结果可能为 304 或 200。

两只钟模型中的缓存新鲜度与校验

缓存的连贯性取决于你让哪一对条件显式化。

八月 14, 2026 · 1 分钟 · XB Field Notes