| Internet 草案 | No-Vary-Search | 2026 年 6 月 |
| Denicola 等人 | 2026 年 12 月 20 日到期 | [页] |
本规范定义了对 HTTP 缓存的扩展,改变 URI 查询 参数影响缓存的方式。¶
本说明将在作为 RFC 发布前移除。¶
本草案的最新修订版可在 https://httpwg.org/http-extensions/draft-ietf-httpbis-no-vary-search.html 找到。 本文档的状态信息可在 https://datatracker.ietf.org/doc/draft-ietf-httpbis-no-vary-search/ 找到。¶
本文档的讨论在 HTTP 工作组邮件列表(mailto:ietf-http-wg@w3.org)上进行, 该邮件列表归档于 https://lists.w3.org/Archives/Public/ietf-http-wg/。 工作组信息可在 https://httpwg.org/ 找到。¶
本草案的源文件和问题跟踪器可在 https://github.com/httpwg/http-extensions/labels/no-vary-search 找到。¶
本 Internet 草案完全按照 BCP 78 和 BCP 79 的规定提交。¶
Internet 草案是互联网工程任务组 (IETF)的工作文档。请注意,其他组织也可以将工作 文档作为 Internet 草案分发。当前 Internet 草案列表位于 https://datatracker.ietf.org/drafts/current/。¶
Internet 草案是有效期最长为六个月的草案文档, 并且可在任何时候由其他文档更新、替换或废止。将 Internet 草案用作参考材料,或以“进行中的工作”之外的方式引用它们, 都是不适当的。¶
本 Internet 草案将于 2026 年 12 月 20 日到期。¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
HTTP 缓存 [HTTP-CACHING] 基于复用在若干缓存键上匹配的资源,其中最重要的是所呈现的目标 URI([HTTP] 的第 7.1 节)。然而, 有时多个 URI 可以表示同一资源。这会导致缓存并不总是像本可做到的那样有用:如果缓存中包含某个 URI 下的响应, 但随后又在另一个 URI 下请求该响应,则缓存版本将被忽略。¶
`No-Vary-Search` 响应标头字段定义了一种缓存扩展,如 [HTTP-CACHING] 的第 4 节 所述, 它处理这一一般问题的一个特定子集,即仅在某些查询参数上不同的 URI 标识同一资源的情形。 它允许资源声明查询组件的某些或全部部分不会在语义上影响所提供的响应,因此可在缓存匹配目的中被忽略。 例如,如果查询参数的顺序不会影响所标识的资源,则可使用以下方式表示¶
如果特定查询参数(例如用于分析的参数)不会在语义上影响所提供的资源, 则可使用以下方式表示¶
如果资源反而想采用基于允许列表的方法,即只有 某些已知查询参数会在语义上影响所提供的响应,它们可以使用¶
请注意,通过发送唯一查询参数以避免取回
缓存响应的“缓存清除”可能会被 No-Vary-Search 标头字段变得无效。¶
第 3 节 使用 [STRUCTURED-FIELDS] 框架定义新的
响应标头字段 No-Vary-Search。第 4 节 和 第
5 节 说明字段值如何在规范中表示的数据模型,以及
将结构化字段解析器的原始输出解析为该数据模型的过程。第 6 节 给出用于比较
两个 URL 在该标头字段影响下是否等价的关键算法;值得注意的是,它依赖
[WHATWG-URL] 中指定的
application/x-www-form-urlencoded 格式
对查询组件给出的键和值分解。(因此,此
标头字段不适用于查询组件不遵循该格式的 URL。)最后,第 7 节 解释如何扩展 [HTTP-CACHING] 的第 4 节,以考虑这种新的等价性。¶
本文档中的关键词“MUST”、“MUST NOT”、 “REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“NOT RECOMMENDED”、 “MAY”和“OPTIONAL”应按 BCP 14 [RFC2119] [RFC8174] 中的描述解释,且仅当它们 如此处所示以全大写形式出现时才如此解释。¶
在本文档中,术语“URI”和“URL”会根据 上下文互换使用。“URI”用于 [URI]、 [HTTP] 和 [HTTP-CACHING] 的上下文,而“URL”用于 [WHATWG-URL] 中指定的算法上下文。¶
本文档还采用 WHATWG 和 W3C 用法中常见的一些约定和记法, 尤其是在涉及算法时。见 [WHATWG-INFRA],特别是:¶
(其他使用的概念会通过内联引用指出。)¶
No-Vary-Search HTTP 标头字段是一个结构化字段 [STRUCTURED-FIELDS],其值 MUST
是一个字典([STRUCTURED-FIELDS] 的第 3.2 节)。¶
它具有以下创作一致性要求:¶
如果存在,key-order 条目的值 MUST
是布尔值([STRUCTURED-FIELDS] 的第 3.3.6
节)。¶
如果存在,params 条目的值 MUST
是字符串内列表([STRUCTURED-FIELDS] 的第 3.1.1
节)。¶
如果存在,except 条目的值 MUST
是字符串内列表([STRUCTURED-FIELDS] 的第 3.1.1
节)。¶
如果 params 条目也存在,则 except 条目 MUST NOT 存在。¶
字典 MAY 包含键不是
key-order、params 和 except 之一的条目,
但它们的含义不由本规范定义。本规范的实现将忽略此类条目(但未来
文档可能会为此类条目赋予含义)。¶
URL 变体配置由以下内容组成:¶
默认 URL 变体 配置是这样一种 URL 变体配置:其 no-vary params 是空列表,vary params 是 wildcard,且 vary on key order 为 true。¶
获取 URL 变体配置 算法(第 5.2 节)确保 所有 URL 变体配置都遵循以下约束:¶
给定 value,要解析 URL 变体 配置:¶
给定一个 response response,要获取 URL 变体 配置:¶
以下从对最终 no-vary params 和 vary params 的影响角度, 说明各种输入如何被解析:¶
| 输入 | 结果 |
|---|---|
No-Vary-Search: params=("a")
|
no-vary params: « "a"
»vary params: wildcard |
No-Vary-Search: except=("x")
|
no-vary params:
wildcard vary params: « " x" »
|
No-Vary-Search: params=()
|
no-vary params:(空
列表) vary params: wildcard |
No-Vary-Search: except=()
|
no-vary params:
wildcard vary params:(空列表) |
以下输入均无效,并会导致返回 默认 URL 变体 配置:¶
No-Vary-Search: key-order="not a boolean"¶
No-Vary-Search: params="not an inner list"¶
No-Vary-Search: params=(not-a-string)¶
No-Vary-Search: params=?0¶
No-Vary-Search: params=?1¶
No-Vary-Search: params=?1, except=("x")¶
No-Vary-Search: params=("a"), except=("x")¶
No-Vary-Search: params=(), except=()¶
No-Vary-Search: except="not an inner list"¶
No-Vary-Search: except=(not-a-string)¶
No-Vary-Search: except=?1¶
以下输入是有效的,但有些不常规。它们与其更常规的形式 一并展示。¶
| 输入 | 常规形式 |
|---|---|
No-Vary-Search: key-order=?1
|
No-Vary-Search: key-order
|
No-Vary-Search: except=("x"), key-order
|
No-Vary-Search: key-order, except=("x")
|
No-Vary-Search: params=()
|
(省略该标头字段) |
No-Vary-Search: key-order=?0
|
(省略该标头字段) |
令 keyBytes 为 keyString 的 同构编码 [WHATWG-INFRA]。¶
将 keyBytes 中的任何 0x2B (+) 替换为 0x20 (SP)。¶
令 keyBytesDecoded 为 keyBytes 的 百分号解码 [WHATWG-URL]。¶
令 keyStringDecoded 为 keyBytesDecoded 的 不带 BOM 的 UTF-8 解码 [WHATWG-ENCODING]。¶
返回 keyStringDecoded。¶
解析键算法允许在 ASCII 结构化标头字段格式中 编码非 ASCII 键字符串,类似于 application/x-www-form-urlencoded 格式 [WHATWG-URL] 如何允许 在 URI(限制为 ASCII 字符)中编码键和值的整个条目列表。例如,¶
将得到一个 URL 变体配置,其 vary params 为 «
"é 気" »。如后面的示例所解释的,等价性测试期间的规范化过程
意味着这会将诸如下列 URI 视为等价:¶
https://example.com/?é 気=1¶
https://example.com/?é+気=2¶
https://example.com/?%C3%A9%20気=3¶
https://example.com/?%C3%A9+%E6%B0%97=4¶
等等,因为它们全都会被解析 [WHATWG-URL] 为具有相同的
键 "é 気"。¶
给定 URL 变体配置 variationConfig,两个 URL [WHATWG-URL] urlA 和 urlB 是按变体配置 模等价的,如果以下 算法返回 true:¶
如果 urlA 和 urlB 的 scheme、username、password、host、port 或 path 不同,则返回 false。¶
如果 variationConfig 等价于 默认 URL 变体配置, 则:¶
在这种情况下,即使 URL 对在对其查询运行 application/x-www-form-urlencoded
解析器 [WHATWG-URL] 后可能看似相同,
例如 https://example.com/a 和 https://example.com/a?,或
https://example.com/foo?a=b&&&c 和
https://example.com/foo?a=b&c=,也会被视为不等价。¶
令 searchParamsA 和 searchParamsB 为空列表。¶
如果 urlA 的 query 不是 null,则将 searchParamsA 设为 给定 urlA 的 query 的 同构编码 [WHATWG-INFRA] 后运行 application/x-www-form-urlencoded 解析器 [WHATWG-URL] 的结果。¶
如果 urlB 的 query 不是 null,则将 searchParamsB 设为 给定 urlB 的 query 的 同构编码 [WHATWG-INFRA] 后运行 application/x-www-form-urlencoded 解析器 [WHATWG-URL] 的结果。¶
如果 variationConfig 的 no-vary params 是列表,则:¶
否则,如果 variationConfig 的 vary params 是列表,则:¶
如果 variationConfig 的 vary on key order 为 false,则:¶
令 keyLessThan 为一个以两个 pair(keyA、valueA)和(keyB、valueB)为输入的算法,该算法 返回 keyA 是否按码元小于 [WHATWG-INFRA] keyB。¶
将 searchParamsA 设为用 keyLessThan 将 searchParamsA 按升序 排序 [WHATWG-INFRA] 的结果。¶
将 searchParamsB 设为用 keyLessThan 将 searchParamsB 按升序 排序 [WHATWG-INFRA] 的结果。¶
如果 searchParamsA 的大小不等于 searchParamsB 的 大小,则返回 false。¶
令 i 为 0。¶
当 i < searchParamsA 的大小时:¶
返回 true。¶
由于 application/x-www-form-urlencoded 解析器规范化查询 字符串的方式,某些查询字符串虽然看起来并不明显等价, 但在解析后最终会被视为等价。¶
因此,例如,给定 No-Vary-Search 的任何非默认值,
如 No-Vary-Search: key-order,我们将得到以下等价关系:¶
| 第一个查询 | 第二个查询 | 说明 |
|---|---|---|
| null |
?
|
null 查询与空字符串解析结果相同 |
?a=x
|
?%61=%78
|
解析会执行百分号解码 |
?a=é
|
?a=%C3%A9
|
解析会执行百分号解码 |
?a=%f6
|
?a=%ef%bf%bd
|
两个值都会解析为 U+FFFD (�) |
?a=x&&&&
|
?a=x
|
解析会按 & 分割并
丢弃空字符串 |
?a=
|
?a
|
二者都解析为对 a
具有空字符串值
|
?a=%20
|
?a= &
|
%20 会解析为 U+0020 SPACE
|
?a=+
|
?a= &
|
+ 会解析为 U+0020 SPACE
|
如果缓存 [HTTP-CACHING] 实现本规范,则 [HTTP-CACHING] 的第 4 节 中的呈现目标 URI 要求:¶
替换为:¶
所呈现的目标 URI([HTTP] 的第 7.1 节)与所存储响应的目标 URI 匹配或 按 URL 变体配置模等价,且¶
缓存实现 MAY 不复用其目标 URI 仅按 URL 变体配置模匹配的已存储响应,如果该缓存最近已 存储了一个响应,该响应:¶
具有一个与所呈现目标 URI 相等的目标 URI(排除 query),并且¶
其 No-Vary-Search 字段具有非空值,并且¶
其 No-Vary-Search 字段值不同于正被考虑复用的
已存储响应。¶
为了帮助提高缓存实现效率,服务器 SHOULD NOT
对某个给定 pathname 的请求随时间发送不同的非空 No-Vary-Search 字段值,
除非需要更新它们处理 query 组件的方式。这样做
会导致使用上述策略的缓存实现错过一些本可被复用的已存储响应。¶
需要注意的主要风险是 URL 不匹配的影响。具体而言,这可能 导致用户看到的响应最初取自一个不同于其悬停链接时所显示 URL 或 URL 栏中所显示 URL 的 URL。¶
然而,由于影响仅限于查询参数,这不会跨越 相关的安全边界,即 origin [HTML]。(或者从 Web 浏览器 安全 UI 的角度,也许只是 host。[WHATWG-URL]) 确实,我们已经给予 origin 对其如何呈现 (URL, response body) 对的完全控制, 包括在客户端通过 history.replaceState() 或 service workers 等技术来控制。¶
本提案邻近高度涉及隐私的导航跟踪领域, 该领域通常使用查询参数传递用户标识符。然而,我们认为本提案 本身没有隐私影响。它不会干扰现有的 导航跟踪缓解措施,也不会干扰任何已知正在考虑的未来措施。事实上,如果 页面在其 URI 中编码用户标识符,本提案提供的唯一能力是 通过阻止服务器处理此类用户 ID(因为服务器会被绕过而使用缓存) 来减少此类用户跟踪。[NAV-TRACKING-MITIGATIONS]¶