同一网页为什么两台设备看到不同版本:HTTP缓存的新鲜度、验证与Vary边界
同一URL不保证命中同一缓存条目。响应年龄、验证器、共享或私有范围及Vary请求字段都会改变设备看到的版本。
手机已经显示资料页的新内容,笔记本却仍停在旧版。换成无痕窗口、按刷新或重新打开链接,结果又不一样。若马上删除全部浏览资料,页面也许更新了,却会丢失判断差异来自哪一层的证据。
同一URL不保证两台设备取得同一个缓存条目。HTTP缓存先选择可能匹配的表示,再判断它是否新鲜、是否需要向源站验证;共享边缘与设备私有缓存还可能处于不同时间线。
先确认两台设备请求的真是同一对象
比较完整URL,包括协议、主机、路径、查询参数和尾斜线。看似相同的页面可能经过跳转落到不同地址,或由应用在后台请求不同API。截图标题相同不能替代请求地址。
记录设备时间、语言、登录状态和网络。登录用户可能取得private响应,访客取得共享缓存;语言和压缩能力也可能参与内容选择。排查目标不是让所有请求字段一致,而是知道哪些差异存在。
先保存旧版与新版的可见版本标识、Date、Age、Cache-Control、ETag、Last-Modified和Vary。证据保存后再复核,避免刷新覆盖最有价值的旧响应。
新鲜度决定能否直接使用旧响应
RFC 9111定义缓存的新鲜度判断:缓存比较响应的新鲜度寿命与当前年龄,符合条件时可以直接复用。笔记本如果较早取得一个寿命较长的响应,手机后来取得新响应,两者在一段时间内可能都按各自状态工作。
Cache-Control的max-age、Expires和响应时间会参与寿命计算。Age传递响应年龄估计,却不是内容发布日期。Age为120表示缓存估计响应已经存在约两分钟,不说明文章在两分钟前发布。
多级缓存会让年龄计算与传递更复杂。没有Age字段也不能证明没有浏览器私有缓存;因此还要看开发工具的资源来源与请求是否真正发出。
陈旧之后可以验证,而不是全部重传
缓存条目变陈旧时,RFC 9111允许使用条件请求向源站验证。ETag可配合If-None-Match,Last-Modified可配合If-Modified-Since。若选定表示未修改,服务器可返回304,缓存继续使用已有正文。
这解释了为什么刷新很快却确实访问了网络:设备只取得验证结果,没有重新传输整份内容。若另一设备没有验证器、缓存策略不同或选中不同表示,就可能下载200新正文。
304并不是“服务器没有内容”,而是针对条件请求的未修改结果。记录请求验证字段和最终显示版本,避免只看状态码做相反判断。
no-cache与no-store不是同一指令
Cloudflare Origin Cache Control文档明确区分两者。no-cache允许缓存保存响应,但要求使用前进行验证;no-store指示缓存不要存储响应。把no-cache当成完全禁用缓存,会误判实际行为。
private通常面向共享缓存限制存储,浏览器仍可能保留私有副本。s-maxage可以为共享缓存设置与浏览器不同的寿命。因此边缘节点已有新版,不代表某台设备的私有条目立即失效。
指令还可能被边缘产品设置影响。检查源站预期之外,要保存客户端实际收到的字段;只有配置截图,没有正式响应,无法证明访问路径采用了哪套规则。
Vary让同一URL拥有多个候选表示
RFC 9111要求缓存选择响应时考虑Vary列出的请求头。若响应写有Vary: Accept-Language,中文请求和英文请求可能对应不同条目;Vary: Accept-Encoding则可能区分压缩表示。
两台设备看到不同内容时,比较Vary以及被列入的实际请求字段。不要只比较浏览器品牌。浏览器可能发送不同语言顺序、编码能力或客户端提示,真正的选择键在请求头。
Vary存在不等于差异一定合理。若源站对选择字段输出错误内容,或某个变体没有更新,仍属于发布或缓存配置问题。它提供调查方向,不替结果背书。
共享边缘与设备缓存形成两条时间线
浏览器可能先从边缘取得响应,再存入自己的缓存。源站发布新版后,边缘条目何时过期、浏览器条目何时过期,以及各自何时验证,可能不同。
建立时间线:源站版本何时上线,边缘响应的Date与Age是多少,两台设备最早何时取得各自版本,后来是否发送验证请求。按时间排列,比反复刷新更容易看见哪一层仍在复用旧表示。
若多个边缘地点版本不同,检查部署是否原子完成、缓存清除是否覆盖正确键和变体。若只有单台设备旧,私有缓存或请求条件差异更值得优先核对。
刷新按钮会改变请求,但不是统一协议开关
普通刷新、强制刷新和重新输入地址在不同浏览器中的请求字段与缓存处理可能不同。不能以按钮名称推断所有中间缓存必然跳过。实际请求与响应字段才是证据。
无痕窗口减少部分既有浏览器状态,却仍会经过共享CDN、企业代理和DNS。它看到新版可以支持“原配置的私有状态有关”,不能直接证明旧设备损坏。
服务工作者也可能拦截请求并使用应用自己的缓存规则。若页面是渐进式应用,检查请求是否由service worker提供,同时保留其版本和更新状态。
版本差异也可能来自源站部署
如果两台设备的Vary选择、响应年龄和验证结果一致,正文仍持续不同,应继续检查多个源站或应用实例是否版本一致。负载均衡可能把请求送到未完成部署的节点。
为页面提供稳定版本标识,例如构建号或内容修订号,并确保它来自实际生成内容。只比较文件修改时间容易受打包与复制影响;只比较视觉文本又可能漏掉API版本。
源站版本分裂与缓存旧版可以同时存在。先按响应头和来源分组,再比较正文标识,避免只选一个原因解释全部观察。
安全复核不先删除个人资料
先在原设备保存证据,再打开隔离浏览器配置测试。若要清除资料,优先限定当前站点,并提前确认会删除登录、离线草稿或本地设置。共享设备还要避免影响其他使用者。
可以发送带条件验证的正常请求观察304或200结果,不通过添加随机查询参数无限制造新缓存键。随机参数可能绕开既有条目,却同时改变服务器和边缘规则,得到不可比的结果。
修复配置后,用原URL、原请求条件和新的隔离会话验证,再观察普通设备是否在合理时间内更新。只证明绕过缓存能看到新版,并不表示既有缓存策略已经正确。
把判断写成可以复查的结论
报告列出完整URL、两台设备的请求条件、响应Date、Age、Cache-Control、ETag、Last-Modified、Vary、状态码、正文版本和观察时间。说明每项字段支持哪一种解释。
若笔记本Age较高且响应仍新鲜,手机取得较新的缓存条目,可以写成缓存时间线差异。若陈旧条目验证后仍取得错误版本,则继续检查验证器或源站。若Vary值不同,比较相应变体的发布状态。
Age是响应年龄估计,不是内容发布时间,也不能独自标定所有中间缓存。通用HTTP规则同样不能证明某一浏览器或CDN的完整内部状态;字段缺失时应保留未知。
缓存只有在当前年龄未超过新鲜度寿命等条件时才可作为新鲜响应直接复用。Vary会把指定请求头加入选择条件,使同一URL存在多个候选表示。no-cache允许存储但要求验证,no-store才指示不存储。
先保存Date、Age、Cache-Control、ETag、Last-Modified和Vary,再比较设备请求条件与验证结果。版本差异并不自动等于设备故障;保留证据后,才能把正常缓存行为、配置错误与源站版本分裂分开处理。
本文核对资料
- IETF/RFC Editor:《HTTP Caching, RFC 9111》,2022-06
- Cloudflare Docs:《Origin Cache Control》,更新于2026-06-30
资料来源
- IETF/RFC Editor:《HTTP Caching, RFC 9111》,发布或更新于 2022-06-01
- Cloudflare Docs:《Origin Cache Control》,发布或更新于 2026-06-30