| draft-ietf-httpbis-no-vary-search-03.txt | draft-ietf-httpbis-no-vary-search-latest.txt | |||
|---|---|---|---|---|
| HyperText Transfer Protocol | HyperText Transfer Protocol | |||
| Internet-Draft | Internet-Draft | |||
| Intended status: Standards Track Google LLC | Intended status: Standards Track , Ed. | |||
| Expires: April 2, 2026 September 29, 2025 | Expires: August 9, 2026 Google LLC | |||
| February 05, 2026 | ||||
| The No-Vary-Search HTTP Response Header Field | The No-Vary-Search HTTP Caching Extension | |||
| draft-ietf-httpbis-no-vary-search-03 | draft-ietf-httpbis-no-vary-search-latest | |||
| Abstract | Abstract | |||
| This specification defines a proposed HTTP response header field for | This specification defines an extension to HTTP Caching, changing how | |||
| changing how URL search parameters impact caching. | URI query parameters impact caching. | |||
| About This Document | About This Document | |||
| This note is to be removed before publishing as an RFC. | This note is to be removed before publishing as an RFC. | |||
| The latest revision of this draft can be found at | The latest revision of this draft can be found at | |||
| <https://httpwg.org/http-extensions/draft-ietf-httpbis-no-vary- | <https://httpwg.org/http-extensions/draft-ietf-httpbis-no-vary- | |||
| search.html>. Status information for this document may be found at | search.html>. Status information for this document may be found at | |||
| <https://datatracker.ietf.org/doc/draft-ietf-httpbis-no-vary- | <https://datatracker.ietf.org/doc/draft-ietf-httpbis-no-vary- | |||
| search/>. | search/>. | |||
| skipping to change at page 1, line 48 ¶ | skipping to change at page 2, line 4 ¶ | |||
| Internet-Drafts are working documents of the Internet Engineering | Internet-Drafts are working documents of the Internet Engineering | |||
| Task Force (IETF). Note that other groups may also distribute | Task Force (IETF). Note that other groups may also distribute | |||
| working documents as Internet-Drafts. The list of current Internet- | working documents as Internet-Drafts. The list of current Internet- | |||
| Drafts is at https://datatracker.ietf.org/drafts/current/. | Drafts is at https://datatracker.ietf.org/drafts/current/. | |||
| Internet-Drafts are draft documents valid for a maximum of six months | Internet-Drafts are draft documents valid for a maximum of six months | |||
| and may be updated, replaced, or obsoleted by other documents at any | and may be updated, replaced, or obsoleted by other documents at any | |||
| time. It is inappropriate to use Internet-Drafts as reference | time. It is inappropriate to use Internet-Drafts as reference | |||
| material or to cite them other than as "work in progress." | material or to cite them other than as "work in progress." | |||
| This Internet-Draft will expire on August 9, 2026. | ||||
| This Internet-Draft will expire on April 2, 2026. | ||||
| Copyright Notice | Copyright Notice | |||
| Copyright (c) 2025 IETF Trust and the persons identified as the | Copyright (c) 2026 IETF Trust and the persons identified as the | |||
| document authors. All rights reserved. | document authors. All rights reserved. | |||
| This document is subject to BCP 78 and the IETF Trust's Legal | This document is subject to BCP 78 and the IETF Trust's Legal | |||
| Provisions Relating to IETF Documents | Provisions Relating to IETF Documents | |||
| (https://trustee.ietf.org/license-info) in effect on the date of | (https://trustee.ietf.org/license-info) in effect on the date of | |||
| publication of this document. Please review these documents | publication of this document. Please review these documents | |||
| carefully, as they describe your rights and restrictions with respect | carefully, as they describe your rights and restrictions with respect | |||
| to this document. Code Components extracted from this document must | to this document. Code Components extracted from this document must | |||
| include Simplified BSD License text as described in Section 4.e of | include Simplified BSD License text as described in Section 4.e of | |||
| the Trust Legal Provisions and are provided without warranty as | the Trust Legal Provisions and are provided without warranty as | |||
| described in the Simplified BSD License. | described in the Simplified BSD License. | |||
| Table of Contents | Table of Contents | |||
| 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2 | 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 3 | |||
| 2. Conventions and Definitions . . . . . . . . . . . . . . . . . 3 | 2. Conventions and Definitions . . . . . . . . . . . . . . . . . 4 | |||
| 3. HTTP header field definition . . . . . . . . . . . . . . . . 4 | 3. HTTP header field definition . . . . . . . . . . . . . . . . 4 | |||
| 4. Data model . . . . . . . . . . . . . . . . . . . . . . . . . 5 | 4. Data model . . . . . . . . . . . . . . . . . . . . . . . . . 5 | |||
| 5. Parsing . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 | 5. Parsing . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 | |||
| 5.1. Parse a URL search variance . . . . . . . . . . . . . . . 6 | 5.1. Parse a URL variation config . . . . . . . . . . . . . . 6 | |||
| 5.2. Obtain a URL search variance . . . . . . . . . . . . . . 9 | 5.2. Obtain a URL variation config . . . . . . . . . . . . . . 9 | |||
| 5.2.1. Examples . . . . . . . . . . . . . . . . . . . . . . 9 | 5.2.1. Examples . . . . . . . . . . . . . . . . . . . . . . 9 | |||
| 5.3. Parse a key . . . . . . . . . . . . . . . . . . . . . . . 11 | 5.3. Parse a key . . . . . . . . . . . . . . . . . . . . . . . 11 | |||
| 5.3.1. Examples . . . . . . . . . . . . . . . . . . . . . . 12 | 5.3.1. Examples . . . . . . . . . . . . . . . . . . . . . . 12 | |||
| 6. Comparing . . . . . . . . . . . . . . . . . . . . . . . . . . 12 | 6. Comparing . . . . . . . . . . . . . . . . . . . . . . . . . . 12 | |||
| 6.1. Examples . . . . . . . . . . . . . . . . . . . . . . . . 15 | 6.1. Examples . . . . . . . . . . . . . . . . . . . . . . . . 15 | |||
| 7. Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 | 7. Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 | |||
| 8. Security Considerations . . . . . . . . . . . . . . . . . . . 18 | 8. Security Considerations . . . . . . . . . . . . . . . . . . . 18 | |||
| 9. Privacy Considerations . . . . . . . . . . . . . . . . . . . 18 | 9. Privacy Considerations . . . . . . . . . . . . . . . . . . . 18 | |||
| 10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 19 | 10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 19 | |||
| 10.1. HTTP Field Names . . . . . . . . . . . . . . . . . . . . 19 | 10.1. HTTP Field Names . . . . . . . . . . . . . . . . . . . . 19 | |||
| 11. References . . . . . . . . . . . . . . . . . . . . . . . . . 19 | 11. References . . . . . . . . . . . . . . . . . . . . . . . . . 19 | |||
| 11.1. Normative References . . . . . . . . . . . . . . . . . . 19 | 11.1. Normative References . . . . . . . . . . . . . . . . . . 19 | |||
| 11.2. Informative References . . . . . . . . . . . . . . . . . 20 | 11.2. Informative References . . . . . . . . . . . . . . . . . 20 | |||
| 11.3. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 20 | 11.3. URIs . . . . . . . . . . . . . . . . . . . . . . . . . . 21 | |||
| Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 | Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 | |||
| Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 22 | Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . . 22 | |||
| Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 22 | Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 23 | |||
| 1. Introduction | 1. Introduction | |||
| HTTP caching [HTTP-CACHING] is based on reusing resources which match | HTTP caching [HTTP-CACHING] is based on reusing resources which match | |||
| across a number of cache keys. One of the most prominent is the | across a number of cache keys, with the most important one being the | |||
| presented target URI (Section 7.1 of [HTTP]). However, sometimes | presented target URI (Section 7.1 of [HTTP]). However, sometimes | |||
| multiple URLs can represent the same resource. This leads to caches | multiple URIs can represent the same resource. This leads to caches | |||
| not always being as helpful as they could be: if the cache contains | not always being as helpful as they could be: if the cache contains a | |||
| the resource under one URI, but the resource is then requested under | response under one URI, but the response is then requested under | |||
| another, the cached version will be ignored. | another, the cached version will be ignored. | |||
| The "No-Vary-Search" HTTP header field tackles a specific subset of | The "No-Vary-Search" response header field defines a caching | |||
| this general problem, for when a resource has multiple URLs which | extension, as described in Section 4 of [HTTP-CACHING], that tackles | |||
| differ only in certain query components. It allows resources to | a specific subset of this general problem, for when different URIs | |||
| declare that some or all parts of the query do not semantically | that only differ in certain query parameters identify the same | |||
| affect the served resource, and thus can be ignored for cache | resource. It allows resources to declare that some or all parts of | |||
| matching purposes. For example, if the order of the query parameter | the query component do not semantically affect the served response, | |||
| keys do not semantically affect the served resource, this is | and thus can be ignored for cache matching purposes. For example, if | |||
| indicated using | the order of the query parameters do not affect which resource is | |||
| identified, this is indicated using | ||||
| No-Vary-Search: key-order | No-Vary-Search: key-order | |||
| If the specific query parameters (e.g., ones indicating something for | If the specific query parameters (e.g., ones indicating something for | |||
| analytics) do not semantically affect the served resource, this is | analytics) do not semantically affect the served resource, this is | |||
| indicated using | indicated using | |||
| No-Vary-Search: params=("utm_source" "utm_medium" "utm_campaign") | No-Vary-Search: params=("utm_source" "utm_medium" "utm_campaign") | |||
| And if the resource instead wants to take an allowlist-based | And if the resource instead wants to take an allowlist-based | |||
| approach, where only certain known query parameters semantically | approach, where only certain known query parameters semantically | |||
| affect the served resource, they can use | affect the served response, they can use | |||
| No-Vary-Search: params, except=("productId") | No-Vary-Search: params, except=("productId") | |||
| Section 3 defines the header, using the [STRUCTURED-FIELDS] | Note that "cache busting" by sending unique query parameters to avoid | |||
| framework. Section 4 and Section 5 illustrate the data model for how | retrieving a cached response can be made ineffective by the "No-Vary- | |||
| the header can be represented in specifications, and the process for | Search" header field. | |||
| parsing the raw output from the structured field parser into that | ||||
| data model. Section 6 gives the key algorithm for comparing if two | Section 3 defines the new response header field "No-Vary-Search", | |||
| URLs are equivalent under the influence of the header; notably, it | using the [STRUCTURED-FIELDS] framework. Section 4 and Section 5 | |||
| leans on the decomposition of the query component into keys and | illustrate the data model for how the field value can be represented | |||
| values given by the application/x-www-form-urlencoded [1] format | in specifications, and the process for parsing the raw output from | |||
| specified in [WHATWG-URL]. (As such, this header is not useful for | the structured field parser into that data model. Section 6 gives | |||
| URLs whose query component does not follow that format.) Finally, | the key algorithm for comparing if two URLs are equivalent under the | |||
| Section 7 explains how to modify [HTTP-CACHING] to take into account | influence of the header field; notably, it leans on the decomposition | |||
| this new equivalence. | of the query component into keys and values given by the application/ | |||
| x-www-form-urlencoded [1] format specified in [WHATWG-URL]. (As | ||||
| such, this header field is not useful for URLs whose query component | ||||
| does not follow that format.) Finally, Section 7 explains how to | ||||
| extend Section 4 of [HTTP-CACHING] to take this new equivalence into | ||||
| account. | ||||
| 2. Conventions and Definitions | 2. Conventions and Definitions | |||
| The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", | |||
| "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and | "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and | |||
| "OPTIONAL" in this document are to be interpreted as described in | "OPTIONAL" in this document are to be interpreted as described in | |||
| BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all | BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all | |||
| capitals, as shown here. | capitals, as shown here. | |||
| In this document, the terms "URI" and "URL" are used interchangeably, | ||||
| depending on context. "URI" is used in the context of [URI], [HTTP], | ||||
| and [HTTP-CACHING], whereas "URL" is used in the context of the | ||||
| algorithms specified in [WHATWG-URL]. | ||||
| This document also adopts some conventions and notation typical in | This document also adopts some conventions and notation typical in | |||
| WHATWG and W3C usage, especially as it relates to algorithms. See | WHATWG and W3C usage, especially as it relates to algorithms. See | |||
| [WHATWG-INFRA], and in particular: | [WHATWG-INFRA], and in particular: | |||
| o its definition of lists, including the list literal notation << 1, | o its definition of lists, including the list literal notation << 1, | |||
| 2, 3 >>. | 2, 3 >>. | |||
| o its definition of strings, including their representation as code | o its definition of strings, including their representation as code | |||
| units. | units. | |||
| skipping to change at page 5, line 10 ¶ | skipping to change at page 5, line 24 ¶ | |||
| true. | true. | |||
| The dictionary MAY contain entries whose keys are not one of "key- | The dictionary MAY contain entries whose keys are not one of "key- | |||
| order", "params", and "except", but their meaning is not defined by | order", "params", and "except", but their meaning is not defined by | |||
| this specification. Implementations of this specification will | this specification. Implementations of this specification will | |||
| ignore such entries (but future documents might assign meaning to | ignore such entries (but future documents might assign meaning to | |||
| such entries). | such entries). | |||
| As always, the authoring conformance requirements are not binding | As always, the authoring conformance requirements are not binding | |||
| on implementations. Implementations instead need to implement the | on implementations. Implementations instead need to implement the | |||
| processing model given by the obtain a URL search variance | processing model for URL variation configurations (configs) given | |||
| algorithm (Section 5.2). | by the obtain a URL variation config algorithm (Section 5.2). | |||
| 4. Data model | 4. Data model | |||
| A _URL search variance_ consists of the following: | A _URL variation config_ consists of the following: | |||
| no-vary params | no-vary params | |||
| either the special value *wildcard* or a list of strings | either the special value *wildcard* or a list of strings | |||
| vary params | vary params | |||
| either the special value *wildcard* or a list of strings | either the special value *wildcard* or a list of strings | |||
| vary on key order | vary on key order | |||
| a boolean | a boolean | |||
| The default URL search variance is a URL search variance whose no- | The default URL variation config is a URL variation config whose no- | |||
| vary params is an empty list, vary params is *wildcard*, and vary on | vary params is an empty list, vary params is *wildcard*, and vary on | |||
| key order is true. | key order is true. | |||
| The obtain a URL search variance algorithm (Section 5.2) ensures that | The obtain a URL variation config algorithm (Section 5.2) ensures | |||
| all URL search variances obey the following constraints: | that all URL variation configs obey the following constraints: | |||
| o vary params is a list if and only if the no-vary params is | o vary params is a list if and only if the no-vary params is | |||
| *wildcard*; and | *wildcard*; and | |||
| o no-vary params is a list if and only if the vary params is | o no-vary params is a list if and only if the vary params is | |||
| *wildcard*. | *wildcard*. | |||
| 5. Parsing | 5. Parsing | |||
| 5.1. Parse a URL search variance | ||||
| To parse a URL search variance given _value_: | 5.1. Parse a URL variation config | |||
| 1. If _value_ is null, then return the default URL search variance. | To parse a URL variation config given _value_: | |||
| 2. Let _result_ be a new URL search variance. | 1. If _value_ is null, then return the default URL variation config. | |||
| 2. Let _result_ be a new URL variation config. | ||||
| 3. Set _result_'s vary on key order to true. | 3. Set _result_'s vary on key order to true. | |||
| 4. If _value_[""key-order""] exists: | 4. If _value_[""key-order""] exists: | |||
| 1. If _value_[""key-order""] is not a boolean, then return the | 1. If _value_[""key-order""] is not a boolean, then return the | |||
| default URL search variance. | default URL variation config. | |||
| 2. Set _result_'s vary on key order to the boolean negation of | 2. Set _result_'s vary on key order to the boolean negation of | |||
| _value_[""key-order""]. | _value_[""key-order""]. | |||
| 5. If _value_[""params""] exists: | 5. If _value_[""params""] exists: | |||
| 1. If _value_[""params""] is a boolean: | 1. If _value_[""params""] is a boolean: | |||
| 1. If _value_[""params""] is true, then: | 1. If _value_[""params""] is true, then: | |||
| skipping to change at page 7, line 16 ¶ | skipping to change at page 7, line 22 ¶ | |||
| 2. Otherwise: | 2. Otherwise: | |||
| 1. Set _result_'s no-vary params to the empty list. | 1. Set _result_'s no-vary params to the empty list. | |||
| 2. Set _result_'s vary params to *wildcard*. | 2. Set _result_'s vary params to *wildcard*. | |||
| 2. Otherwise, if _value_[""params""] is an array: | 2. Otherwise, if _value_[""params""] is an array: | |||
| 1. If any item in _value_[""params""] is not a string, then | 1. If any item in _value_[""params""] is not a string, then | |||
| return the default URL search variance. | return the default URL variation config. | |||
| 2. Set _result_'s no-vary params to the result of applying | 2. Set _result_'s no-vary params to the result of applying | |||
| parse a key (Section 5.3) to each item in | parse a key (Section 5.3) to each item in | |||
| _value_[""params""]. | _value_[""params""]. | |||
| 3. Set _result_'s vary params to *wildcard*. | 3. Set _result_'s vary params to *wildcard*. | |||
| 3. Otherwise, return the default URL search variance. | 3. Otherwise, return the default URL variation config. | |||
| 6. If _value_[""except""] exists: | 6. If _value_[""except""] exists: | |||
| 1. If _value_[""params""] is not true, then return the default | 1. If _value_[""params""] is not true, then return the default | |||
| URL search variance. | URL variation config. | |||
| 2. If _value_[""except""] is not an array, then return the | 2. If _value_[""except""] is not an array, then return the | |||
| default URL search variance. | default URL variation config. | |||
| 3. If any item in _value_[""except""] is not a string, then | 3. If any item in _value_[""except""] is not a string, then | |||
| return the default URL search variance. | return the default URL variation config. | |||
| 4. Set _result_'s vary params to the result of applying parse a | 4. Set _result_'s vary params to the result of applying parse a | |||
| key (Section 5.3) to each item in _value_[""except""]. | key (Section 5.3) to each item in _value_[""except""]. | |||
| 7. Return _result_. | 7. Return _result_. | |||
| In general, this algorithm is strict and tends to return the | In general, this algorithm is strict and tends to return the | |||
| default URL search variance whenever it sees something it doesn't | default URL variation config whenever it sees something it doesn't | |||
| recognize. This is because the default URL search variance | recognize. This is because the default URL variation config | |||
| behavior will just cause fewer cache hits, which is an acceptable | behavior will just cause fewer cache hits, which is an acceptable | |||
| fallback behavior. | fallback behavior. | |||
| However, unrecognized keys at the top level are ignored, to make | However, unrecognized keys at the top level are ignored, to make | |||
| it easier to extend this specification in the future. To avoid | it easier to extend this specification in the future. To avoid | |||
| misbehavior with existing client software, such extensions will | misbehavior with existing client software, such extensions will | |||
| likely expand, rather than reduce, the set of requests that a | likely expand, rather than reduce, the set of requests that a | |||
| cached response can match. | cached response can match. | |||
| The input to this algorithm is generally obtained by parsing a | The input to this algorithm is generally obtained by parsing a | |||
| structured field (Section 4.2 of [STRUCTURED-FIELDS]) using | structured field (Section 4.2 of [STRUCTURED-FIELDS]) using | |||
| field_type "dictionary". | field_type "dictionary". | |||
| 5.2. Obtain a URL search variance | 5.2. Obtain a URL variation config | |||
| To obtain a URL search variance given a response [2] _response_: | To obtain a URL variation config given a response [2] _response_: | |||
| 1. Let _fieldValue_ be the result of getting a structured field | 1. Let _fieldValue_ be the result of getting a structured field | |||
| value [3] [FETCH] given `"No-Vary-Search"` and ""dictionary"" | value [3] [FETCH] given `"No-Vary-Search"` and ""dictionary"" | |||
| from _response_'s header list. | from _response_'s header list. | |||
| 2. Return the result of parsing a URL search variance (Section 5.1) | 2. Return the result of parsing a URL variation config (Section 5.1) | |||
| given _fieldValue_. | given _fieldValue_. | |||
| 5.2.1. Examples | 5.2.1. Examples | |||
| The following illustrates how various inputs are parsed, in terms of | The following illustrates how various inputs are parsed, in terms of | |||
| their impacting on the resulting no-vary params and vary params: | their impact on the resulting no-vary params and vary params: | |||
| +-----------------------------+-------------------------------------+ | +-----------------------------+-------------------------------------+ | |||
| | Input | Result | | | Input | Result | | |||
| +-----------------------------+-------------------------------------+ | +-----------------------------+-------------------------------------+ | |||
| | "No-Vary-Search: params" | no-vary params: *wildcard* vary | | | "No-Vary-Search: params" | no-vary params: *wildcard* vary | | |||
| | | params: (empty list) | | | | params: (empty list) | | |||
| | | | | | | | | |||
| | "No-Vary-Search: | no-vary params: << ""a"" >> vary | | | "No-Vary-Search: | no-vary params: << ""a"" >> vary | | |||
| | params=("a")" | params: *wildcard* | | | params=("a")" | params: *wildcard* | | |||
| | | | | | | | | |||
| | "No-Vary-Search: params, | no-vary params: *wildcard* vary | | | "No-Vary-Search: params, | no-vary params: *wildcard* vary | | |||
| | except=("x")" | params: << ""x"" >> | | | except=("x")" | params: << ""x"" >> | | |||
| +-----------------------------+-------------------------------------+ | +-----------------------------+-------------------------------------+ | |||
| The following inputs are all invalid and will cause the default URL | The following inputs are all invalid and will cause the default URL | |||
| search variance to be returned: | variation config to be returned: | |||
| o "No-Vary-Search: unknown-key" | ||||
| o "No-Vary-Search: key-order="not a boolean"" | o "No-Vary-Search: key-order="not a boolean"" | |||
| o "No-Vary-Search: params="not a boolean or inner list"" | o "No-Vary-Search: params="not a boolean or inner list"" | |||
| o "No-Vary-Search: params=(not-a-string)" | o "No-Vary-Search: params=(not-a-string)" | |||
| o "No-Vary-Search: params=("a"), except=("x")" | o "No-Vary-Search: params=("a"), except=("x")" | |||
| o "No-Vary-Search: params=(), except=()" | o "No-Vary-Search: params=(), except=()" | |||
| o "No-Vary-Search: params=?0, except=("x")" | o "No-Vary-Search: params=?0, except=("x")" | |||
| o "No-Vary-Search: params, except=(not-a-string)" | o "No-Vary-Search: params, except=(not-a-string)" | |||
| skipping to change at page 11, line 15 ¶ | skipping to change at page 11, line 15 ¶ | |||
| +---------------------------------+---------------------------------+ | +---------------------------------+---------------------------------+ | |||
| | Input | Conventional form | | | Input | Conventional form | | |||
| +---------------------------------+---------------------------------+ | +---------------------------------+---------------------------------+ | |||
| | "No-Vary-Search: params=?1" | "No-Vary-Search: params" | | | "No-Vary-Search: params=?1" | "No-Vary-Search: params" | | |||
| | | | | | | | | |||
| | "No-Vary-Search: key-order=?1" | "No-Vary-Search: key-order" | | | "No-Vary-Search: key-order=?1" | "No-Vary-Search: key-order" | | |||
| | | | | | | | | |||
| | "No-Vary-Search: params, key- | "No-Vary-Search: key-order, | | | "No-Vary-Search: params, key- | "No-Vary-Search: key-order, | | |||
| | order, except=("x")" | params, except=("x")" | | | order, except=("x")" | params, except=("x")" | | |||
| | | | | | | | | |||
| | "No-Vary-Search: params=?0" | (omit the header) | | | "No-Vary-Search: params=?0" | (omit the header field) | | |||
| | | | | | | | | |||
| | "No-Vary-Search: params=()" | (omit the header) | | | "No-Vary-Search: params=()" | (omit the header field) | | |||
| | | | | | | | | |||
| | "No-Vary-Search: key-order=?0" | (omit the header) | | | "No-Vary-Search: key-order=?0" | (omit the header field) | | |||
| +---------------------------------+---------------------------------+ | +---------------------------------+---------------------------------+ | |||
| 5.3. Parse a key | 5.3. Parse a key | |||
| To parse a key given an ASCII string _keyString_: | To parse a key given an ASCII string _keyString_: | |||
| 1. Let _keyBytes_ be the isomorphic encoding [4] [WHATWG-INFRA] of | 1. Let _keyBytes_ be the isomorphic encoding [4] [WHATWG-INFRA] of | |||
| _keyString_. | _keyString_. | |||
| 2. Replace any 0x2B (+) in _keyBytes_ with 0x20 (SP). | 2. Replace any 0x2B (+) in _keyBytes_ with 0x20 (SP). | |||
| skipping to change at page 12, line 8 ¶ | skipping to change at page 12, line 8 ¶ | |||
| _keyBytes_. | _keyBytes_. | |||
| 4. Let _keyStringDecoded_ be the UTF-8 decoding without BOM [6] | 4. Let _keyStringDecoded_ be the UTF-8 decoding without BOM [6] | |||
| [WHATWG-ENCODING] of _keyBytesDecoded_. | [WHATWG-ENCODING] of _keyBytesDecoded_. | |||
| 5. Return _keyStringDecoded_. | 5. Return _keyStringDecoded_. | |||
| 5.3.1. Examples | 5.3.1. Examples | |||
| The parse a key algorithm allows encoding non-ASCII key strings in | The parse a key algorithm allows encoding non-ASCII key strings in | |||
| the ASCII structured header format, similar to how the application/x- | the ASCII structured header field format, similar to how the | |||
| www-form-urlencoded [7] format [WHATWG-URL] allows encoding an entire | application/x-www-form-urlencoded [7] format [WHATWG-URL] allows | |||
| entry list of keys and values in ASCII URL format. For example, | encoding an entire entry list of keys and values in a URI (which is | |||
| restricted to ASCII characters). For example, | ||||
| No-Vary-Search: params=("%C3%A9+%E6%B0%97") | No-Vary-Search: params=("%C3%A9+%E6%B0%97") | |||
| will result in a URL search variance whose vary params are << ""e | will result in a URL variation config whose vary params are << ""e | |||
| 気"" >>. As explained in a later example, the canonicalization | 気"" >>. As explained in a later example, the canonicalization | |||
| process during equivalence testing means this will treat as | process during equivalence testing means this will treat as | |||
| equivalent URL strings such as: | equivalent URIs such as: | |||
| o "https://example.com/?e 気=1" | o "https://example.com/?e 気=1" | |||
| o "https://example.com/?e+気=2" | o "https://example.com/?e+気=2" | |||
| o "https://example.com/?%C3%A9%20気=3" | o "https://example.com/?%C3%A9%20気=3" | |||
| o "https://example.com/?%C3%A9+%E6%B0%97=4" | o "https://example.com/?%C3%A9+%E6%B0%97=4" | |||
| and so on, since they all are parsed [8] [WHATWG-URL] to having the | and so on, since they all are parsed [8] [WHATWG-URL] to having the | |||
| same key ""e 気"". | same key ""e 気"". | |||
| 6. Comparing | 6. Comparing | |||
| Two URLs [9] [WHATWG-URL] _urlA_ and _urlB_ are _equivalent modulo | Two URLs [9] [WHATWG-URL] _urlA_ and _urlB_ are _equivalent modulo | |||
| search variance_ given a URL search variance _searchVariance_ if the | variation config_ given a URL variation config _variationConfig_ if | |||
| following algorithm returns true: | the following algorithm returns true: | |||
| 1. If the scheme, username, password, host, port, or path of _urlA_ | 1. If the scheme, username, password, host, port, or path of _urlA_ | |||
| and _urlB_ differ, then return false. | and _urlB_ differ, then return false. | |||
| 2. If _searchVariance_ is equivalent to the default URL search | 2. If _variationConfig_ is equivalent to the default URL variation | |||
| variance, then: | config, then: | |||
| 1. If _urlA_'s query equals _urlB_'s query, then return true. | 1. If _urlA_'s query equals _urlB_'s query, then return true. | |||
| 2. Return false. | 2. Return false. | |||
| In this case, even URL pairs that might appear the same after | In this case, even URL pairs that might appear the same after | |||
| running the application/x-www-form-urlencoded parser [10] | running the application/x-www-form-urlencoded parser [10] | |||
| [WHATWG-URL] on their queries, such as "https://example.com/a" | [WHATWG-URL] on their queries, such as "https://example.com/a" | |||
| and "https://example.com/a?", or "https://example.com/ | and "https://example.com/a?", or "https://example.com/ | |||
| foo?a=b&&&c" and "https://example.com/foo?a=b&c=", will be | foo?a=b&&&c" and "https://example.com/foo?a=b&c=", will be | |||
| skipping to change at page 13, line 28 ¶ | skipping to change at page 13, line 28 ¶ | |||
| 4. If _urlA_'s query is not null, then set _searchParamsA_ to the | 4. If _urlA_'s query is not null, then set _searchParamsA_ to the | |||
| result of running the application/x-www-form-urlencoded parser | result of running the application/x-www-form-urlencoded parser | |||
| [11] [WHATWG-URL] given the isomorphic encoding [12] | [11] [WHATWG-URL] given the isomorphic encoding [12] | |||
| [WHATWG-INFRA] of _urlA_'s query. | [WHATWG-INFRA] of _urlA_'s query. | |||
| 5. If _urlB_'s query is not null, then set _searchParamsB_ to the | 5. If _urlB_'s query is not null, then set _searchParamsB_ to the | |||
| result of running the application/x-www-form-urlencoded parser | result of running the application/x-www-form-urlencoded parser | |||
| [13] [WHATWG-URL] given the isomorphic encoding [14] | [13] [WHATWG-URL] given the isomorphic encoding [14] | |||
| [WHATWG-INFRA] of _urlB_'s query. | [WHATWG-INFRA] of _urlB_'s query. | |||
| 6. If _searchVariance_'s no-vary params is a list, then: | 6. If _variationConfig_'s no-vary params is a list, then: | |||
| 1. Set _searchParamsA_ to a list containing those items _pair_ | 1. Set _searchParamsA_ to a list containing those items _pair_ | |||
| in _searchParamsA_ where _searchVariance_'s no-vary params | in _searchParamsA_ where _variationConfig_'s no-vary params | |||
| does not contain _pair_[0]. | does not contain _pair_[0]. | |||
| 2. Set _searchParamsB_ to a list containing those items _pair_ | 2. Set _searchParamsB_ to a list containing those items _pair_ | |||
| in _searchParamsB_ where _searchVariance_'s no-vary params | in _searchParamsB_ where _variationConfig_'s no-vary params | |||
| does not contain _pair_[0]. | does not contain _pair_[0]. | |||
| 7. Otherwise, if _searchVariance_'s vary params is a list, then: | 7. Otherwise, if _variationConfig_'s vary params is a list, then: | |||
| 1. Set _searchParamsA_ to a list containing those items _pair_ | 1. Set _searchParamsA_ to a list containing those items _pair_ | |||
| in _searchParamsA_ where _searchVariance_'s vary params | in _searchParamsA_ where _variationConfig_'s vary params | |||
| contains _pair_[0]. | contains _pair_[0]. | |||
| 2. Set _searchParamsB_ to a list containing those items _pair_ | 2. Set _searchParamsB_ to a list containing those items _pair_ | |||
| in _searchParamsB_ where _searchVariance_'s vary params | in _searchParamsB_ where _variationConfig_'s vary params | |||
| contains _pair_[0]. | contains _pair_[0]. | |||
| 8. If _searchVariance_'s vary on key order is false, then: | 8. If _variationConfig_'s vary on key order is false, then: | |||
| 1. Let _keyLessThan_ be an algorithm taking as inputs two pairs | 1. Let _keyLessThan_ be an algorithm taking as inputs two pairs | |||
| (_keyA_, _valueA_) and (_keyB_, _valueB_), which returns | (_keyA_, _valueA_) and (_keyB_, _valueB_), which returns | |||
| whether _keyA_ is code unit less than [15] [WHATWG-INFRA] | whether _keyA_ is code unit less than [15] [WHATWG-INFRA] | |||
| _keyB_. | _keyB_. | |||
| 2. Set _searchParamsA_ to the result of sorting _searchParamsA_ | 2. Set _searchParamsA_ to the result of sorting _searchParamsA_ | |||
| in ascending order with _keyLessThan_. | in ascending order with _keyLessThan_. | |||
| 3. Set _searchParamsB_ to the result of sorting _searchParamsB_ | 3. Set _searchParamsB_ to the result of sorting _searchParamsB_ | |||
| skipping to change at page 15, line 28 ¶ | skipping to change at page 16, line 5 ¶ | |||
| Due to how the application/x-www-form-urlencoded parser canonicalizes | Due to how the application/x-www-form-urlencoded parser canonicalizes | |||
| query strings, there are some cases where query strings which do not | query strings, there are some cases where query strings which do not | |||
| appear obviously equivalent, will end up being treated as equivalent | appear obviously equivalent, will end up being treated as equivalent | |||
| after parsing. | after parsing. | |||
| So, for example, given any non-default value for "No-Vary-Search", | So, for example, given any non-default value for "No-Vary-Search", | |||
| such as "No-Vary-Search: key-order", we will have the following | such as "No-Vary-Search: key-order", we will have the following | |||
| equivalences: | equivalences: | |||
| https://example.com https://example.com/? | +------------+----------------+-------------------------------------+ | |||
| A null query is parsed the same as an empty string | | First | Second Query | Explanation | | |||
| | Query | | | | ||||
| https://example.com/?a=x https://example.com/?%61=%78 | +------------+----------------+-------------------------------------+ | |||
| Parsing performs percent-decoding | | null | "?" | A null query is parsed the same as | | |||
| | | | an empty string | | ||||
| https://example.com/?a=e https://example.com/?a=%C3%A9 | | | | | | |||
| Parsing performs percent-decoding | | "?a=x" | "?%61=%78" | Parsing performs percent-decoding | | |||
| | | | | | ||||
| https://example.com/?a=%f6 https://example.com/?a=%ef%bf%bd | | "?a=e" | "?a=%C3%A9" | Parsing performs percent-decoding | | |||
| Both values are parsed as U+FFFD (�) | | | | | | |||
| | "?a=%f6" | "?a=%ef%bf%bd" | Both values are parsed as U+FFFD | | ||||
| https://example.com/?a=x&&&& https://example.com/?a=x | | | | (�) | | |||
| Parsing splits on "&" and discards empty strings | | | | | | |||
| | "?a=x&&&&" | "?a=x" | Parsing splits on "&" and discards | | ||||
| https://example.com/?a= https://example.com/?a | | | | empty strings | | |||
| Both parse as having an empty string value for "a" | | | | | | |||
| | "?a=" | "?a" | Both parse as having an empty | | ||||
| https://example.com/?a=%20 https://example.com/?a=+ | | | | string value for "a" | | |||
| https://example.com/?a= & | | | | | | |||
| "+" and "%20" are both parsed as U+0020 SPACE | | "?a=%20" | "?a= &" | "%20" is parsed as U+0020 SPACE | | |||
| | | | | | ||||
| | "?a=+" | "?a= &" | "+" is parsed as U+0020 SPACE | | ||||
| +------------+----------------+-------------------------------------+ | ||||
| 7. Caching | 7. Caching | |||
| If a cache [HTTP-CACHING] implements this specification, the | If a cache [HTTP-CACHING] implements this specification, the | |||
| presented target URI requirement in Section 4 of [HTTP-CACHING] is | presented target URI requirement in Section 4 of [HTTP-CACHING]: | |||
| replaced with: | ||||
| o one of the following: | the presented target URI (Section 7.1 of [HTTP]) and that of the | |||
| stored response match, and | ||||
| * the presented target URI (Section 7.1 of [HTTP]) and that of | is replaced with: | |||
| the stored response match, or | ||||
| * the presented target URI and that of the stored response are | the presented target URI (Section 7.1 of [HTTP]) and that of the | |||
| equivalent modulo search variance (Section 6), given the | stored response match or are equivalent modulo URL variation | |||
| variance obtained (Section 5.2) from the stored response. | config, and | |||
| Cache implementations MAY fail to reuse a stored response whose | Cache implementations MAY fail to reuse a stored response whose | |||
| target URI matches _only_ modulo URL search variance, if the cache | target URI matches _only_ modulo URL variation config, if the cache | |||
| has more recently stored a response which: | has more recently stored a response which: | |||
| o has a target URI which is equal to the presented target URI, | o has a target URI which is equal to the presented target URI, | |||
| excluding the query, and | excluding the query, and | |||
| o has a non-empty value for the "No-Vary-Search" field, and | o has a non-empty value for the "No-Vary-Search" field, and | |||
| o has a "No-Vary-Search" field value different from the stored | o has a "No-Vary-Search" field value different from the stored | |||
| response being considered for reuse. | response being considered for reuse. | |||
| skipping to change at page 17, line 35 ¶ | skipping to change at page 18, line 5 ¶ | |||
| 4. Let simplifiedURL be the result of simplifying | 4. Let simplifiedURL be the result of simplifying | |||
| presentedTargetURI according to lastNVS (by removing query | presentedTargetURI according to lastNVS (by removing query | |||
| parameters which are not significant, and stable sorting | parameters which are not significant, and stable sorting | |||
| parameters by key, if key order is to be be ignored). | parameters by key, if key order is to be be ignored). | |||
| 5. Let nvsMatch be cache[simplifiedURL]. If it does not exist, | 5. Let nvsMatch be cache[simplifiedURL]. If it does not exist, | |||
| return null. (It is assumed that this was written when | return null. (It is assumed that this was written when | |||
| storing in the cache, in addition to the exact URL.) | storing in the cache, in addition to the exact URL.) | |||
| 6. Let searchVariance be obtained (Section 5.2) from nvsMatch. | 6. Let variationConfig be obtained (Section 5.2) from nvsMatch. | |||
| 7. If nvsMatch's target URI and presentedTargetURI are not | 7. If nvsMatch's target URI and presentedTargetURI are not | |||
| equivalent modulo search variance (Section 6) given | equivalent modulo URL variation config (Section 6) given | |||
| searchVariance, then return null. | variationConfig, then return null. | |||
| 8. If nvsMatch is a stored response that can be reused, return | 8. If nvsMatch is a stored response that can be reused, return | |||
| it. Otherwise, return null. | it. Otherwise, return null. | |||
| To aid cache implementation efficiency, servers SHOULD NOT send | To aid cache implementation efficiency, servers SHOULD NOT send | |||
| different non-empty values for the "No-Vary-Search" field in response | different non-empty values for the "No-Vary-Search" field in response | |||
| to requests for a given pathname over time, unless there is a need to | to requests for a given pathname over time, unless there is a need to | |||
| update how they handle the query component. Doing so would cause | update how they handle the query component. Doing so would cause | |||
| cache implementations that use a strategy like the above to miss some | cache implementations that use a strategy like the above to miss some | |||
| stored responses that could otherwise have been reused. | stored responses that could otherwise have been reused. | |||
| skipping to change at page 18, line 31 ¶ | skipping to change at page 18, line 33 ¶ | |||
| The main risk to be aware of is the impact of mismatched URLs. In | The main risk to be aware of is the impact of mismatched URLs. In | |||
| particular, this could cause the user to see a response that was | particular, this could cause the user to see a response that was | |||
| originally fetched from a URL different from the one displayed when | originally fetched from a URL different from the one displayed when | |||
| they hovered a link, or the URL displayed in the URL bar. | they hovered a link, or the URL displayed in the URL bar. | |||
| However, since the impact is limited to query parameters, this does | However, since the impact is limited to query parameters, this does | |||
| not cross the relevant security boundary, which is the origin [16] | not cross the relevant security boundary, which is the origin [16] | |||
| [HTML]. (Or perhaps just the host [17], from the perspective of web | [HTML]. (Or perhaps just the host [17], from the perspective of web | |||
| browser security UI [18]. [WHATWG-URL]) Indeed, we have already | browser security UI [18]. [WHATWG-URL]) Indeed, we have already | |||
| given origins complete control over how they present the (URL, | given origins complete control over how they present the (URL, | |||
| reponse body) pair, including on the client side via technology such | response body) pair, including on the client side via technology such | |||
| as history.replaceState() [19] or service workers. | as history.replaceState() [19] or service workers. | |||
| 9. Privacy Considerations | 9. Privacy Considerations | |||
| This proposal is adjacent to the highly-privacy-relevant space of | This proposal is adjacent to the highly-privacy-relevant space of | |||
| navigational tracking [20], which often uses query parameters to pass | navigational tracking [20], which often uses query parameters to pass | |||
| along user identifiers. However, we believe this proposal itself | along user identifiers. However, we believe this proposal itself | |||
| does not have privacy impacts. It does not interfere with existing | does not have privacy impacts. It does not interfere with existing | |||
| navigational tracking mitigations [21], or any known future ones | navigational tracking mitigations [21], or any known future ones | |||
| being contemplated. Indeed, if a page were to encode user | being contemplated. Indeed, if a page were to encode user | |||
| identifiers in its URL, the only ability this proposal gives is to | identifiers in its URI, the only ability this proposal gives is to | |||
| _reduce_ such user tracking by preventing server processing of such | _reduce_ such user tracking by preventing server processing of such | |||
| user IDs (since the server is bypassed in favor of the cache). | user IDs (since the server is bypassed in favor of the cache). | |||
| [NAV-TRACKING-MITIGATIONS] | [NAV-TRACKING-MITIGATIONS] | |||
| 10. IANA Considerations | 10. IANA Considerations | |||
| IANA should do the following: | ||||
| 10.1. HTTP Field Names | 10.1. HTTP Field Names | |||
| Enter the following into the Hypertext Transfer Protocol (HTTP) Field | IANA is requested to enter the following into the Hypertext Transfer | |||
| Name Registry: | Protocol (HTTP) Field Name Registry | |||
| (https://www.iana.org/assignments/http-fields/http-fields.xhtml | ||||
| [22]): | ||||
| Field Name "No-Vary-Search" | Field Name: "No-Vary-Search" | |||
| Status permanent | Status: permanent | |||
| Structured Type Dictionary | Structured Type: Dictionary | |||
| Reference this document | Reference: this document | |||
| Comments (none) | Comments: (none) | |||
| 11. References | 11. References | |||
| 11.1. Normative References | 11.1. Normative References | |||
| [FETCH] van Kesteren, A., "Fetch Living Standard", n.d., | [FETCH] van Kesteren, A., "Fetch Living Standard", n.d., | |||
| <https://fetch.spec.whatwg.org/>. | <https://fetch.spec.whatwg.org/>. | |||
| WHATWG | WHATWG | |||
| skipping to change at page 20, line 7 ¶ | skipping to change at page 20, line 11 ¶ | |||
| Requirement Levels", BCP 14, RFC 2119, | Requirement Levels", BCP 14, RFC 2119, | |||
| DOI 10.17487/RFC2119, March 1997, | DOI 10.17487/RFC2119, March 1997, | |||
| <https://www.rfc-editor.org/info/rfc2119>. | <https://www.rfc-editor.org/info/rfc2119>. | |||
| [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC | [RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC | |||
| 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, | 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, | |||
| May 2017, <https://www.rfc-editor.org/info/rfc8174>. | May 2017, <https://www.rfc-editor.org/info/rfc8174>. | |||
| [STRUCTURED-FIELDS] | [STRUCTURED-FIELDS] | |||
| Nottingham, M. and P. Kamp, "Structured Field Values for | Nottingham, M. and P. Kamp, "Structured Field Values for | |||
| HTTP", RFC 8941, DOI 10.17487/RFC8941, February 2021, | HTTP", RFC 9651, DOI 10.17487/RFC9651, September 2024, | |||
| <https://www.rfc-editor.org/info/rfc8941>. | <https://www.rfc-editor.org/info/rfc9651>. | |||
| [URI] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform | ||||
| Resource Identifier (URI): Generic Syntax", STD 66, | ||||
| RFC 3986, DOI 10.17487/RFC3986, January 2005, | ||||
| <https://www.rfc-editor.org/info/rfc3986>. | ||||
| [WHATWG-ENCODING] | [WHATWG-ENCODING] | |||
| van Kesteren, A., "Encoding Living Standard", n.d., | van Kesteren, A., "Encoding Living Standard", n.d., | |||
| <https://encoding.spec.whatwg.org/>. | <https://encoding.spec.whatwg.org/>. | |||
| WHATWG | WHATWG | |||
| [WHATWG-INFRA] | [WHATWG-INFRA] | |||
| van Kesteren, A. and D. Denicola, "Infra Living Standard", | van Kesteren, A. and D. Denicola, "Infra Living Standard", | |||
| n.d., <https://infra.spec.whatwg.org/>. | n.d., <https://infra.spec.whatwg.org/>. | |||
| skipping to change at page 21, line 43 ¶ | skipping to change at page 22, line 5 ¶ | |||
| [19] https://html.spec.whatwg.org/multipage/nav-history- | [19] https://html.spec.whatwg.org/multipage/nav-history- | |||
| apis.html#dom-history-replacestate | apis.html#dom-history-replacestate | |||
| [20] https://privacycg.github.io/nav-tracking- | [20] https://privacycg.github.io/nav-tracking- | |||
| mitigations/#terminology | mitigations/#terminology | |||
| [21] https://privacycg.github.io/nav-tracking-mitigations/#deployed- | [21] https://privacycg.github.io/nav-tracking-mitigations/#deployed- | |||
| mitigations | mitigations | |||
| [22] https://www.iana.org/assignments/http-fields/http-fields.xhtml | ||||
| Index | Index | |||
| D | D | |||
| default URL search variance 5-9, 12 | default URL variation config 5-9, 12 | |||
| E | E | |||
| equivalent modulo search variance 12 | equivalent modulo variation config 12 | |||
| O | O | |||
| obtain a URL search variance 5, 9 | obtain a URL variation config 5-6, 9 | |||
| P | P | |||
| parse a URL search variance 6, 9 | parse a URL variation config 6, 9 | |||
| parse a key 7-8, 11-12 | parse a key 7-8, 11-12 | |||
| Acknowledgments | Acknowledgments | |||
| This document benefited from valuable reviews and suggestions by: | This document benefited from valuable reviews and suggestions by: | |||
| o Adam Rice | o Adam Rice | |||
| o Julian Reschke | o Julian Reschke | |||
| skipping to change at line 814 ¶ | skipping to change at page 23, line 16 ¶ | |||
| Domenic Denicola | Domenic Denicola | |||
| Google LLC | Google LLC | |||
| Email: d@domenic.me | Email: d@domenic.me | |||
| Jeremy Roman | Jeremy Roman | |||
| Google LLC | Google LLC | |||
| Email: jbroman@chromium.org | Email: jbroman@chromium.org | |||
| Nidhi Jaju (editor) | ||||
| Google LLC | ||||
| Email: nidhijaju@chromium.org | ||||
| End of changes. 76 change blocks. | ||||
| 137 lines changed or deleted | 157 lines changed or added | |||
This html diff was produced by rfcdiff 1.48. The latest version is available from http://tools.ietf.org/tools/rfcdiff/ | ||||