摘要

  • No-Vary-Search 允许源站声明查询键或键顺序不影响缓存匹配。支持该字段的缓存可以据此复用,但新鲜度、验证、Vary、授权和其他 RFC 9111 条件一个都不会消失。
  • 完整 URL 仍会经过浏览器、CDN、代理与日志。错误的等价声明可能让一次请求绕过源站,把另一个用户上下文中的已存响应直接交付出去。

第一次请求是 /report?id=88&utm_source=mail,第二次是 /report?utm_source=partner&id=88。应用团队知道推广来源不会改变报告,参数顺序也无意义;缓存只看到了两个不同的目标 URI。让缓存依据样本自行猜测二者相同,看似智能,实质是让最不了解业务语义的一层取得合并身份的权力。

2026 年 8 月 17 日发布的 draft-ietf-httpbis-no-vary-search-09 给出了一套显式语言。截至 8 月 29 日,它仍是 HTTPBIS 的活跃互联网草案,预期进入 Proposed Standard,IESG 状态为已批准、等待公告并由领域主任后续处理。它还不是 RFC,IANA 的 HTTP 字段名称登记表也尚未收录 No-Vary-Search。

这一状态不是脚注。当前文本足以支持有边界的互操作实验,却不能被描述成已经冻结的标准,更不能证明某个 CDN 已正确实现、某项分类没有遗漏,或复用必然改善了用户体验。

精确匹配是安全起点

RFC 9111 要求缓存复用已存响应前逐项满足条件:方法允许复用,目标 URI 匹配,Vary 指定的请求字段匹配,响应仍然新鲜或已成功验证,而且请求与响应的缓存控制均允许这样做。查询部分不同,通常就意味着目标 URI 不同。

这种保守不是低效,而是拒绝替应用解释参数。account=alice、sig=... 与 campaign=summer 在 HTTP 层都只是字符。键名像“跟踪”不等于它只负责跟踪;抽样得到相同响应体也不能证明它从未参与路由、授权、计费或撤销。

新字段只扩展目标 URI 的匹配步骤。它不会让过期响应变新,不会取消 Vary,不会把私有响应变成共享内容,也不会授予访问资格。即使查询被判定等价,其余 RFC 9111 检查仍须全部通过。

一个小字典承载一项大判断

字段值是 RFC 9651 定义的结构化字段字典。key-order 是布尔值,说明查询键顺序是否无关;params 列出匹配时忽略的键;except 采用相反表达,只有列出的键继续造成差异。params 与 except 不得同时出现。

声明权属于源站,因为源站拥有应用语义。中间设备不得插入、删除或修改该字段,除非它在这次响应中扮演源站。若 CDN 根据流量观察自行生成规则,权力关系就倒置了:边缘层不再执行源站知识,而是在定义应用身份。

解析失败时,规范选择封闭失败。字段缺失、格式错误或内部矛盾,都会回到默认配置:完整查询必须精确匹配,顺序仍然重要。代价是少一些命中,不是少一道隔离。未知字典键会被忽略,因此未来扩展只能扩大等价集合;若要缩小集合,必须另设字段,避免旧实现因为忽略新限制而过度复用。

真正比较的不是屏幕上的字符串

等价范围不能跨越 scheme、主机、端口或路径。一个路径的声明不能把另一个路径纳入,也不能跨源取响应。进入查询内部后,非默认配置使用 WHATWG 的 application/x-www-form-urlencoded 模型解析,删除被忽略的键值对或只保留 except 集合,必要时按键排序,再比较键和值;重复键会保留。

因此,肉眼不同不代表算法不同。百分号编码会被解码,+ 会变成空格,空片段按表单规则处理。无效 UTF-8 可能解码成替代字符 U+FFFD,使不同原始字节落到同一个键上。与此同时,算法不做 Unicode 规范化,NFC 与 NFD 两种写法仍会被视为不同。

若签名验证针对原始字节,路由器依据重复键的先后顺序,或空值承担同意语义,这些细节就是权限边界。草案明确指出,不符合 form-urlencoded 模型的查询不适合使用该字段。RFC 6943 对标识符比较的警告在这里变成可执行风险:不同层若采用不同的规范化和作用域,假阳性会直接决定交付哪个响应体。

源站给出选择,缓存保留决定

支持该草案的缓存可以采用扩展比较,但没有义务采用。它可以先查精确键,可以建立由最新策略推导出的简化索引,也可以拒绝一次模策略命中。源站提供知识;承担复用后果的缓存仍然保留本地决定。这是自愿采用,而不是远程指挥。

策略还会变化。同一主机与路径的已存响应若带有互相冲突的非空配置,草案允许缓存偏向 Date 更新的那一份,以便逐步收敛。但“更新”只是线索,不是“正确”的证明。错误规则也可能最新,不同边缘节点也可能在不同时间看到它。

失效规则并未自动扩大。一次改变状态的方法发生后,缓存可以把按新字段等价的 URI 一并失效,但并非必须。于是,读取时被视为等价的两个条目,写入后可能保留不同版本。若团队靠增加查询参数“破缓存”,而这个键又被声明不造成差异,发布就可能继续命中旧内容。内容寻址的路径或文件名是另一种更清楚的部署选择。

缓存等价不是隐私清洗

浏览器仍显示完整 URL,也可能把它写入历史;CDN 和前向代理仍收到标识符;访问日志、分析系统和下游服务仍可能保存它。字段改变的是缓存匹配,不是地址重写、重定向或去标识化。

私有浏览器缓存可能在第二次请求时不再访问源站,从而减少源站处理某些跟踪参数。共享缓存仍看得到请求,而且错误配置扩大了同一已存响应可触达的请求集合。把这项机制宣传为“删除跟踪参数”,会同时掩盖数据仍在流动和复用边界已经变化。

安全禁区必须写死:凡是控制身份、授权、签名验证、同意、路由、审计、计费、撤销或安全复用所需处理的参数,都不得被宣布为无差异。响应像素相同,不代表访问资格相同;页面正文相同,也不代表一次归因或审计事件可以省略。

共享缓存中的最坏结果,是把为 Bob 取得的内容交给 Alice。private、正确分区与其他缓存控制依然重要,但它们不能为虚假声明免责。源站对分类负责,缓存对命中瞬间的实际隔离负责,权力必须跟随后果分配。

证据要覆盖那次没有抵达源站的请求

标准记录能够证明语法、预期算法与公开安全要求,却不能证明某个参数真的无关、某个节点实现了 -09、一次命中确实发生,或用户边界从未被越过。

可审计记录至少应包含:用户看到的请求 URL、已存响应的 URL 与原始字段、解析后的配置、变换后参与比较的键值对、最终选中的条目、其余 RFC 9111 检查、用户和缓存分区上下文、命中或未命中、是否绕过源站,以及实际交付响应体的指纹。只有命中率,无法区分正确抽象与高效泄露。

Lu Heng 关于最小共同规范与本地未来决定的框架恰好解释了这条权力链:共同层只提供字典和比较流程;查询键的语义继续由应用本地判断;缓存是否采用仍是自愿决定。他对运行代码优先的论述进一步要求把证据放在实际选中条目与交付结果上,而不是停在配置界面。

数据主权的技术现实与实践现实也不能混为一谈。源站在技术上控制字段,浏览器、CDN 与缓存却分别掌握标识符、日志、复用和响应体;源站甚至看不到被缓存解决的第二次请求。治理必须追踪这些实际保管权。

正确的非对称性很朴素:不确定时可以多一次未命中,不能少一道用户隔离。