<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<!-- generated by https://github.com/cabo/kramdown-rfc version 1.7.11 (Ruby 3.2.4) -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" ipr="trust200902" docName="draft-wicg-http-no-vary-search-00" category="info" consensus="true" submissionType="IETF" tocInclude="true" sortRefs="true" symRefs="true" version="3">
  <!-- xml2rfc v2v3 conversion 3.21.0 -->
  <front>
    <title>No-Vary-Search</title>
    <seriesInfo name="Internet-Draft" value="draft-wicg-http-no-vary-search-00"/>
    <author fullname="Domenic Denicola">
      <organization>Google LLC</organization>
      <address>
        <email>d@domenic.me</email>
      </address>
    </author>
    <author fullname="Jeremy Roman">
      <organization>Google LLC</organization>
      <address>
        <email>jbroman@chromium.org</email>
      </address>
    </author>
    <date year="2024" month="June" day="04"/>
    <area>Applications</area>
    <workgroup>HyperText Transfer Protocol</workgroup>
    <keyword>http</keyword>
    <keyword>caching</keyword>
    <abstract>
      <?line 76?>

<t>A proposed HTTP header field for changing how URL search parameters impact caching.</t>
    </abstract>
    <note removeInRFC="true">
      <name>About This Document</name>
      <t>
        The latest revision of this draft can be found at <eref target="https://jeremyroman.github.io/http-no-vary-search/draft-wicg-http-no-vary-search.html"/>.
        Status information for this document may be found at <eref target="https://datatracker.ietf.org/doc/draft-wicg-http-no-vary-search/"/>.
      </t>
      <t>Source for this draft and an issue tracker can be found at
        <eref target="https://github.com/jeremyroman/http-no-vary-search"/>.</t>
    </note>
  </front>
  <middle>
    <?line 80?>

<section anchor="conventions-and-definitions">
      <name>Conventions and Definitions</name>
      <t>The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>", "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>", "<bcp14>SHALL
NOT</bcp14>", "<bcp14>SHOULD</bcp14>", "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>", "<bcp14>NOT RECOMMENDED</bcp14>",
"<bcp14>MAY</bcp14>", and "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/> when, and only when, they
appear in all capitals, as shown here.</t>
      <?line -18?>

<t>This document also adopts some conventions and notation typical in WHATWG and W3C usage, especially as it relates to algorithms. See <xref target="WHATWG-INFRA"/>.</t>
    </section>
    <section anchor="http-header-field-definition">
      <name>HTTP header field definition</name>
      <t>The <tt>No-Vary-Search</tt> HTTP header field is a structured field <xref target="STRUCTURED-FIELDS"/> whose value must be a dictionary (<xref section="3.2" sectionFormat="of" target="STRUCTURED-FIELDS"/>).</t>
      <!--
TODO: probably give some more introductory non-normative text. Look at what other HTTP field defintions do.
-->

<t>It has the following authoring conformance requirements:</t>
      <ul spacing="normal">
        <li>
          <t>The dictionary must only contain entries whose keys are one of <tt>key-order</tt>, <tt>params</tt>, <tt>except</tt>.</t>
        </li>
        <li>
          <t>If present, the <tt>key-order</tt> entry's value must be a boolean (<xref section="3.3.6" sectionFormat="of" target="STRUCTURED-FIELDS"/>).</t>
        </li>
        <li>
          <t>If present, the <tt>params</tt> entry's value must be either a boolean (<xref section="3.3.6" sectionFormat="of" target="STRUCTURED-FIELDS"/>) or an inner list (<xref section="3.1.1" sectionFormat="of" target="STRUCTURED-FIELDS"/>).</t>
        </li>
        <li>
          <t>If present, the <tt>except</tt> entry's value must be an inner list (<xref section="3.1.1" sectionFormat="of" target="STRUCTURED-FIELDS"/>).</t>
        </li>
        <li>
          <t>The <tt>except</tt> entry must only be present if the <tt>params</tt> entry is also present, and the <tt>params</tt> entry's value is the boolean value true.</t>
        </li>
      </ul>
      <aside>
        <t>As always, the authoring conformance requirements are not binding on implementations. Implementations instead need to implement the processing model given by the <iref item="obtain a URL search variance"/><xref target="obtain-a-url-search-variance" format="none">obtain a URL search variance</xref> algorithm (<xref target="obtain-a-url-search-variance"/>).</t>
      </aside>
    </section>
    <section anchor="data-model">
      <name>Data model</name>
      <t>A <em>URL search variance</em> consists of the following:</t>
      <dl newline="true">
        <dt>no-vary params</dt>
        <dd>
          <t>either the special value <strong>wildcard</strong> or a list of strings</t>
        </dd>
        <dt>vary params</dt>
        <dd>
          <t>either the special value <strong>wildcard</strong> or a list of strings</t>
        </dd>
        <dt>vary on key order</dt>
        <dd>
          <t>a boolean</t>
        </dd>
      </dl>
      <t><iref item="default URL search variance" primary="true"/>
The <em><iref item="default URL search variance"/>default URL search variance</em> is a URL search variance whose no-vary params is an empty list, vary params is <strong>wildcard</strong>, and vary on key order is true.</t>
      <t>The <iref item="obtain a URL search variance"/><xref target="obtain-a-url-search-variance" format="none">obtain a URL search variance</xref> algorithm (<xref target="obtain-a-url-search-variance"/>) ensures that all URL search variances obey the following constraints:</t>
      <ul spacing="normal">
        <li>
          <t>vary params is a list if and only if the no-vary params is <strong>wildcard</strong>; and</t>
        </li>
        <li>
          <t>no-vary params is a list if and only if the vary params is <strong>wildcard</strong>.</t>
        </li>
      </ul>
    </section>
    <section anchor="parsing">
      <name>Parsing</name>
      <section anchor="parse-a-url-search-variance">
        <name>Parse a URL search variance</name>
        <t><iref item="parse a URL search variance" primary="true"/>
To <em><iref item="parse a URL search variance"/><xref target="parse-a-url-search-variance" format="none">parse a URL search variance</xref></em> given <em>value</em>:</t>
        <ol spacing="normal" type="1"><li>
            <t>If <em>value</em> is null, then return the <iref item="default URL search variance"/>default URL search variance.</t>
          </li>
          <li>
            <t>If <em>value</em>'s keys contains anything other than "<tt>key-order</tt>", "<tt>params</tt>", or "<tt>except</tt>", then return the <iref item="default URL search variance"/>default URL search variance.</t>
          </li>
          <li>
            <t>Let <em>result</em> be a new URL search variance.</t>
          </li>
          <li>
            <t>Set <em>result</em>'s vary on key order to true.</t>
          </li>
          <li>
            <t>If <em>value</em>["<tt>key-order</tt>"] exists:
            </t>
            <ol spacing="normal" type="1"><li>
                <t>If <em>value</em>["<tt>key-order</tt>"] is not a boolean, then return the <iref item="default URL search variance"/>default URL search variance.</t>
              </li>
              <li>
                <t>Set <em>result</em>'s vary on key order to the boolean negation of <em>value</em>["<tt>key-order</tt>"].</t>
              </li>
            </ol>
          </li>
          <li>
            <t>If <em>value</em>["<tt>params</tt>"] exists:
            </t>
            <ol spacing="normal" type="1"><li>
                <t>If <em>value</em>["<tt>params</tt>"] is a boolean:
                </t>
                <ol spacing="normal" type="1"><li>
                    <t>If <em>value</em>["<tt>params</tt>"] is true, then:
                    </t>
                    <ol spacing="normal" type="1"><li>
                        <t>Set <em>result</em>'s no-vary params to <strong>wildcard</strong>.</t>
                      </li>
                      <li>
                        <t>Set <em>result</em>'s vary params to the empty list.</t>
                      </li>
                    </ol>
                  </li>
                  <li>
                    <t>Otherwise:
                    </t>
                    <ol spacing="normal" type="1"><li>
                        <t>Set <em>result</em>'s no-vary params to the empty list.</t>
                      </li>
                      <li>
                        <t>Set <em>result</em>'s vary params to <strong>wildcard</strong>.</t>
                      </li>
                    </ol>
                  </li>
                </ol>
              </li>
              <li>
                <t>Otherwise, if <em>value</em>["<tt>params</tt>"] is an array:
                </t>
                <ol spacing="normal" type="1"><li>
                    <t>If any item in <em>value</em>["<tt>params</tt>"] is not a string, then return the <iref item="default URL search variance"/>default URL search variance.</t>
                  </li>
                  <li>
                    <t>Set <em>result</em>'s no-vary params to the result of applying <iref item="parse a key"/><xref target="parse-a-key" format="none">parse a key</xref> (<xref target="parse-a-key"/>) to each item in <em>value</em>["<tt>params</tt>"].</t>
                  </li>
                  <li>
                    <t>Set <em>result</em>'s vary params to <strong>wildcard</strong>.</t>
                  </li>
                </ol>
              </li>
              <li>
                <t>Otherwise, return the <iref item="default URL search variance"/>default URL search variance.</t>
              </li>
            </ol>
          </li>
          <li>
            <t>If <em>value</em>["<tt>except</tt>"] exists:
            </t>
            <ol spacing="normal" type="1"><li>
                <t>If <em>value</em>["<tt>params</tt>"] is not true, then return the <iref item="default URL search variance"/>default URL search variance.</t>
              </li>
              <li>
                <t>If <em>value</em>["<tt>except</tt>"] is not an array, then return the <iref item="default URL search variance"/>default URL search variance.</t>
              </li>
              <li>
                <t>If any item in <em>value</em>["<tt>except</tt>"] is not a string, then return the <iref item="default URL search variance"/>default URL search variance.</t>
              </li>
              <li>
                <t>Set <em>result</em>'s vary params to the result of applying <iref item="parse a key"/><xref target="parse-a-key" format="none">parse a key</xref> (<xref target="parse-a-key"/>) to each item in <em>value</em>["<tt>except</tt>"].</t>
              </li>
            </ol>
          </li>
          <li>
            <t>Return <em>result</em>.</t>
          </li>
        </ol>
        <aside>
          <t>In general, this algorithm is strict and tends to return the <iref item="default URL search variance"/>default URL search variance whenever it sees something it doesn't recognize. This is because the <iref item="default URL search variance"/>default URL search variance behavior will just cause fewer cache hits, which is an acceptable fallback behavior.</t>
        </aside>
        <aside>
          <t>The input to this algorithm is generally obtained by parsing a structured field (<xref section="4.2" sectionFormat="of" target="STRUCTURED-FIELDS"/>) using field_type "dictionary".</t>
        </aside>
      </section>
      <section anchor="obtain-a-url-search-variance">
        <name>Obtain a URL search variance</name>
        <t><iref item="obtain a URL search variance" primary="true"/>
To <em><iref item="obtain a URL search variance"/><xref target="obtain-a-url-search-variance" format="none">obtain a URL search variance</xref></em> given a <eref target="https://fetch.spec.whatwg.org/#concept-response">response</eref> <em>response</em>:</t>
        <ol spacing="normal" type="1"><li>
            <t>Let <em>fieldValue</em> be the result of <eref target="https://fetch.spec.whatwg.org/#concept-header-list-get-structured-header">getting a structured field value</eref> <xref target="FETCH"/> given `<tt>No-Vary-Search</tt>` and "<tt>dictionary</tt>" from <em>response</em>'s header list.</t>
          </li>
          <li>
            <t>Return the result of parsing a URL search variance (<xref target="parse-a-url-search-variance"/>) given <em>fieldValue</em>. <iref item="parse a URL search variance"/></t>
          </li>
        </ol>
        <section anchor="examples">
          <name>Examples</name>
          <t>The following illustrates how various inputs are parsed, in terms of their impacting on the resulting no-vary params and vary params:</t>
          <table>
            <thead>
              <tr>
                <th align="left">Input</th>
                <th align="left">Result</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>No-Vary-Search: params</tt></td>
                <td align="left">no-vary params: <strong>wildcard</strong><br/>vary params: (empty list)</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>No-Vary-Search: params=("a")</tt></td>
                <td align="left">no-vary params: « "<tt>a</tt>" »<br/>vary params: <strong>wildcard</strong></td>
              </tr>
              <tr>
                <td align="left">
                  <tt>No-Vary-Search: params, except=("x")</tt></td>
                <td align="left">no-vary params: <strong>wildcard</strong><br/>vary params: « "<tt>x</tt>" »</td>
              </tr>
            </tbody>
          </table>
          <t>The following inputs are all invalid and will cause the <iref item="default URL search variance"/>default URL search variance to be returned:</t>
          <ul spacing="compact">
            <li>
              <t><tt>No-Vary-Search: unknown-key</tt></t>
            </li>
            <li>
              <t><tt>No-Vary-Search: key-order="not a boolean"</tt></t>
            </li>
            <li>
              <t><tt>No-Vary-Search: params="not a boolean or inner list"</tt></t>
            </li>
            <li>
              <t><tt>No-Vary-Search: params=(not-a-string)</tt></t>
            </li>
            <li>
              <t><tt>No-Vary-Search: params=("a"), except=("x")</tt></t>
            </li>
            <li>
              <t><tt>No-Vary-Search: params=(), except=()</tt></t>
            </li>
            <li>
              <t><tt>No-Vary-Search: params=?0, except=("x")</tt></t>
            </li>
            <li>
              <t><tt>No-Vary-Search: params, except=(not-a-string)</tt></t>
            </li>
            <li>
              <t><tt>No-Vary-Search: params, except="not an inner list"</tt></t>
            </li>
            <li>
              <t><tt>No-Vary-Search: params, except=?1</tt></t>
            </li>
            <li>
              <t><tt>No-Vary-Search: except=("x")</tt></t>
            </li>
            <li>
              <t><tt>No-Vary-Search: except=()</tt></t>
            </li>
          </ul>
          <t>The following inputs are valid, but somewhat unconventional. They are shown alongside their more conventional form.</t>
          <table>
            <thead>
              <tr>
                <th align="left">Input</th>
                <th align="left">Conventional form</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td align="left">
                  <tt>No-Vary-Search: params=?1</tt></td>
                <td align="left">
                  <tt>No-Vary-Search: params</tt></td>
              </tr>
              <tr>
                <td align="left">
                  <tt>No-Vary-Search: key-order=?1</tt></td>
                <td align="left">
                  <tt>No-Vary-Search: key-order</tt></td>
              </tr>
              <tr>
                <td align="left">
                  <tt>No-Vary-Search: params, key-order, except=("x")</tt></td>
                <td align="left">
                  <tt>No-Vary-Search: key-order, params, except=("x")</tt></td>
              </tr>
              <tr>
                <td align="left">
                  <tt>No-Vary-Search: params=?0</tt></td>
                <td align="left">(omit the header)</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>No-Vary-Search: params=()</tt></td>
                <td align="left">(omit the header)</td>
              </tr>
              <tr>
                <td align="left">
                  <tt>No-Vary-Search: key-order=?0</tt></td>
                <td align="left">(omit the header)</td>
              </tr>
            </tbody>
          </table>
        </section>
      </section>
      <section anchor="parse-a-key">
        <name>Parse a key</name>
        <t><iref item="parse a key" primary="true"/>
To <em><iref item="parse a key"/><xref target="parse-a-key" format="none">parse a key</xref></em> given an ASCII string <em>keyString</em>:</t>
        <ol spacing="normal" type="1"><li>
            <t>Let <em>keyBytes</em> be the <eref target="https://infra.spec.whatwg.org/#isomorphic-encode">isomorphic encoding</eref> <xref target="WHATWG-INFRA"/> of <em>keyString</em>.</t>
          </li>
          <li>
            <t>Replace any 0x2B (+) in <em>keyBytes</em> with 0x20 (SP).</t>
          </li>
          <li>
            <t>Let <em>keyBytesDecoded</em> be the <eref target="https://url.spec.whatwg.org/#percent-decode">percent-decoding</eref> <xref target="WHATWG-URL"/> of <em>keyBytes</em>.</t>
          </li>
          <li>
            <t>Let <em>keyStringDecoded</em> be the <eref target="https://encoding.spec.whatwg.org/#utf-8-decode-without-bom">UTF-8 decoding without BOM</eref> <xref target="WHATWG-ENCODING"/> of <em>keyBytesDecoded</em>.</t>
          </li>
          <li>
            <t>Return <em>keyStringDecoded</em>.</t>
          </li>
        </ol>
        <section anchor="examples-1">
          <name>Examples</name>
          <t>The <iref item="parse a key"/><xref target="parse-a-key" format="none">parse a key</xref> algorithm allows encoding non-ASCII key strings in the ASCII structured header format, similar to how the <eref target="https://url.spec.whatwg.org/#concept-urlencoded">application/x-www-form-urlencoded</eref> format <xref target="WHATWG-URL"/> allows encoding an entire entry list of keys and values in ASCII URL format. For example,</t>
          <sourcecode type="http-message"><![CDATA[
No-Vary-Search: params=("%C3%A9+%E6%B0%97")
]]></sourcecode>
          <t>will result in a URL search variance whose vary params are « "<tt>é 気</tt>" ». As explained in a later example, the canonicalization process during equivalence testing means this will treat as equivalent URL strings such as:</t>
          <!-- link "a later example" and "equivalence testing" -->

<ul spacing="normal">
            <li>
              <t><tt>https://example.com/?é 気=1</tt></t>
            </li>
            <li>
              <t><tt>https://example.com/?é+気=2</tt></t>
            </li>
            <li>
              <t><tt>https://example.com/?%C3%A9%20気=3</tt></t>
            </li>
            <li>
              <t><tt>https://example.com/?%C3%A9+%E6%B0%97=4</tt></t>
            </li>
          </ul>
          <t>and so on, since they all are <eref target="https://url.spec.whatwg.org/#concept-urlencoded-parser">parsed</eref> <xref target="WHATWG-URL"/> to having the same key "<tt>é 気</tt>".</t>
        </section>
      </section>
    </section>
    <section anchor="comparing">
      <name>Comparing</name>
      <t><iref item="equivalent modulo search variance" primary="true"/>
Two <eref target="https://url.spec.whatwg.org/#concept-url">URLs</eref> <xref target="WHATWG-URL"/> <em>urlA</em> and <em>urlB</em> are <em>equivalent modulo search variance</em> given a URL search variance <em>searchVariance</em> if the following algorithm returns true:</t>
      <ol spacing="normal" type="1"><li>
          <t>If the scheme, username, password, host, port, or path of <em>urlA</em> and <em>urlB</em> differ, then return false.</t>
        </li>
        <li>
          <t>If <em>searchVariance</em> is equivalent to the <iref item="default URL search variance"/>default URL search variance, then:  </t>
          <ol spacing="normal" type="1"><li>
              <t>If <em>urlA</em>'s query equals <em>urlB</em>'s query, then return true.</t>
            </li>
            <li>
              <t>Return false.</t>
            </li>
          </ol>
          <t>
In this case, even URL pairs that might appear the same after running the <eref target="https://url.spec.whatwg.org/#concept-urlencoded-parser">application/x-www-form-urlencoded parser</eref> <xref target="WHATWG-URL"/> on their queries, such as <tt>https://example.com/a</tt> and <tt>https://example.com/a?</tt>, or <tt>https://example.com/foo?a=b&amp;&amp;&amp;c</tt> and <tt>https://example.com/foo?a=b&amp;c=</tt>, will be treated as inequivalent.</t>
        </li>
        <li>
          <t>Let <em>searchParamsA</em> and <em>searchParamsB</em> be empty lists.</t>
        </li>
        <li>
          <t>If <em>wrlA</em>'s query is not null, then set <em>searchParamsA</em> to the result of running the <eref target="https://url.spec.whatwg.org/#concept-urlencoded-parser">application/x-www-form-urlencoded parser</eref> <xref target="WHATWG-URL"/> given the <eref target="https://infra.spec.whatwg.org/#isomorphic-encode">isomorphic encoding</eref> <xref target="WHATWG-INFRA"/> of <em>urlA</em>'s query.</t>
        </li>
        <li>
          <t>If <em>wrlB</em>'s query is not null, then set <em>searchParamsB</em> to the result of running the <eref target="https://url.spec.whatwg.org/#concept-urlencoded-parser">application/x-www-form-urlencoded parser</eref> <xref target="WHATWG-URL"/> given the <eref target="https://infra.spec.whatwg.org/#isomorphic-encode">isomorphic encoding</eref> <xref target="WHATWG-INFRA"/> of <em>urlB</em>'s query.</t>
        </li>
        <li>
          <t>If <em>searchVariance</em>'s no-vary params is a list, then:  </t>
          <ol spacing="normal" type="1"><li>
              <t>Set <em>searchParamsA</em> to a list containing those items <em>pair</em> in <em>searchParamsA</em> where <em>searchVariance</em>'s no-vary params does not contain <em>pair</em>[0].</t>
            </li>
            <li>
              <t>Set <em>searchParamsB</em> to a list containing those items <em>pair</em> in <em>searchParamsB</em> where <em>searchVariance</em>'s no-vary params does not contain <em>pair</em>[0].</t>
            </li>
          </ol>
        </li>
        <li>
          <t>Otherwise, if <em>searchVariance</em>'s vary params is a list, then:  </t>
          <ol spacing="normal" type="1"><li>
              <t>Set <em>searchParamsA</em> to a list containing those items <em>pair</em> in <em>searchParamsA</em> where <em>searchVariance</em>'s vary params contains <em>pair</em>[0].</t>
            </li>
            <li>
              <t>Set <em>searchParamsB</em> to a list containing those items <em>pair</em> in <em>searchParamsB</em> where <em>searchVariance</em>'s vary params contains <em>pair</em>[0].</t>
            </li>
          </ol>
        </li>
        <li>
          <t>If <em>searchVariance</em>'s vary on key order is false, then:  </t>
          <ol spacing="normal" type="1"><li>
              <t>Let <em>keyLessThan</em> be an algorithm taking as inputs two pairs (<em>keyA</em>, <em>valueA</em>) and (<em>keyB</em>, <em>valueB</em>), which returns whether <em>keyA</em> is <eref target="https://infra.spec.whatwg.org/#code-unit-less-than">code unit less than</eref> <xref target="WHATWG-INFRA"/> <em>keyB</em>.</t>
            </li>
            <li>
              <t>Set <em>searchParamsA</em> to the result of sorting <em>searchParamsA</em> in ascending order with <em>keyLessThan</em>.</t>
            </li>
            <li>
              <t>Set <em>searchParamsB</em> to the result of sorting <em>searchParamsB</em> in ascending order with <em>keyLessThan</em>.</t>
            </li>
          </ol>
        </li>
        <li>
          <t>If <em>searchParamsA</em>'s size is not equal to <em>searchParamsB</em>'s size, then return false.</t>
        </li>
        <li>
          <t>Let <em>i</em> be 0.</t>
        </li>
        <li>
          <t>While <em>i</em> &lt; <em>searchParamsA</em>'s size:  </t>
          <ol spacing="normal" type="1"><li>
              <t>If <em>searchParamsA</em>[<em>i</em>][0] does not equal <em>searchParamsB</em>[<em>i</em>][0], then return false.</t>
            </li>
            <li>
              <t>If <em>searchParamsA</em>[<em>i</em>][1] does not equal <em>searchParamsB</em>[<em>i</em>][1], then return false.</t>
            </li>
            <li>
              <t>Set <em>i</em> to <em>i</em> + 1.</t>
            </li>
          </ol>
        </li>
        <li>
          <t>Return true.</t>
        </li>
      </ol>
      <section anchor="examples-2">
        <name>Examples</name>
        <t>Due to how the application/x-www-form-urlencoded parser canonicalizes query strings, there are some cases where query strings which do not appear obviously equivalent, will end up being treated as equivalent after parsing.</t>
        <t>So, for example, given any non-default value for <tt>No-Vary-Search</tt>, such as <tt>No-Vary-Search: key-order</tt>, we will have the following equivalences:</t>
        <dl newline="true">
          <dt>
        <tt>https://example.com</tt>
            <br/>
            <tt>https://example.com/?</tt>
          </dt>
          <dd>A null query is parsed the same as an empty string</dd>
          <dt>
        <tt>https://example.com/?a=x</tt>
            <br/>
            <tt>https://example.com/?%61=%78</tt>
          </dt>
          <dd>Parsing performs percent-decoding</dd>
          <dt>
        <tt>https://example.com/?a=é</tt>
            <br/>
            <tt>https://example.com/?a=%C3%A9</tt>
          </dt>
          <dd>Parsing performs percent-decoding</dd>
          <dt>
        <tt>https://example.com/?a=%f6</tt>
            <br/>
            <tt>https://example.com/?a=%ef%bf%bd</tt>
          </dt>
          <dd>Both values are parsed as U+FFFD (�)</dd>
          <dt>
        <tt>https://example.com/?a=x&amp;&amp;&amp;&amp;</tt>
            <br/>
            <tt>https://example.com/?a=x</tt>
          </dt>
          <dd>Parsing splits on     <tt>&amp;</tt>
 and discards empty strings</dd>
          <dt>
        <tt>https://example.com/?a=</tt>
            <br/>
            <tt>https://example.com/?a</tt>
          </dt>
          <dd>Both parse as having an empty string value for     <tt>a</tt>
          </dd>
          <dt>
        <tt>https://example.com/?a=%20</tt>
            <br/>
            <tt>https://example.com/?a=+</tt>
            <br/>
            <tt>https://example.com/?a= &amp;</tt>
          </dt>
          <dd>
            <tt>+</tt>
 and     <tt>%20</tt>
 are both parsed as U+0020 SPACE</dd>
        </dl>
      </section>
    </section>
    <section anchor="security-considerations">
      <name>Security Considerations</name>
      <t>TODO Security</t>
    </section>
    <section anchor="privacy-considerations">
      <name>Privacy Considerations</name>
      <t>TODO Privacy</t>
    </section>
    <section anchor="iana-considerations">
      <name>IANA Considerations</name>
      <t>TODO IANA</t>
    </section>
  </middle>
  <back>
    <references anchor="sec-normative-references">
      <name>Normative References</name>
      <reference anchor="FETCH" target="https://fetch.spec.whatwg.org/">
        <front>
          <title>Fetch Living Standard</title>
          <author initials="A." surname="van Kesteren" fullname="Anne van Kesteren">
            <organization>Apple Inc.</organization>
          </author>
          <date>n.d.</date>
        </front>
        <annotation>WHATWG</annotation>
      </reference>
      <reference anchor="STRUCTURED-FIELDS">
        <front>
          <title>Structured Field Values for HTTP</title>
          <author fullname="M. Nottingham" initials="M." surname="Nottingham"/>
          <author fullname="P-H. Kamp" surname="P-H. Kamp"/>
          <date month="February" year="2021"/>
          <abstract>
            <t>This document describes a set of data types and associated algorithms that are intended to make it easier and safer to define and handle HTTP header and trailer fields, known as "Structured Fields", "Structured Headers", or "Structured Trailers". It is intended for use by specifications of new HTTP fields that wish to use a common syntax that is more restrictive than traditional HTTP field values.</t>
          </abstract>
        </front>
        <seriesInfo name="RFC" value="8941"/>
        <seriesInfo name="DOI" value="10.17487/RFC8941"/>
      </reference>
      <reference anchor="WHATWG-ENCODING" target="https://encoding.spec.whatwg.org/">
        <front>
          <title>Encoding Living Standard</title>
          <author initials="A." surname="van Kesteren" fullname="Anne van Kesteren">
            <organization>Apple Inc.</organization>
          </author>
          <date>n.d.</date>
        </front>
        <annotation>WHATWG</annotation>
      </reference>
      <reference anchor="WHATWG-INFRA" target="https://infra.spec.whatwg.org/">
        <front>
          <title>Infra Living Standard</title>
          <author initials="A." surname="van Kesteren" fullname="Anne van Kesteren">
            <organization>Apple Inc.</organization>
          </author>
          <author initials="D." surname="Denicola" fullname="Domenic Denicola">
            <organization>Google LLC</organization>
          </author>
          <date>n.d.</date>
        </front>
        <annotation>WHATWG</annotation>
      </reference>
      <reference anchor="WHATWG-URL" target="https://url.spec.whatwg.org/">
        <front>
          <title>URL Living Standard</title>
          <author initials="A." surname="van Kesteren" fullname="Anne van Kesteren">
            <organization>Apple Inc.</organization>
          </author>
          <date>n.d.</date>
        </front>
        <annotation>WHATWG</annotation>
      </reference>
      <reference anchor="RFC2119">
        <front>
          <title>Key words for use in RFCs to Indicate Requirement Levels</title>
          <author fullname="S. Bradner" initials="S." surname="Bradner"/>
          <date month="March" year="1997"/>
          <abstract>
            <t>In many standards track documents several words are used to signify the requirements in the specification. These words are often capitalized. This document defines these words as they should be interpreted in IETF documents. This document specifies an Internet Best Current Practices for the Internet Community, and requests discussion and suggestions for improvements.</t>
          </abstract>
        </front>
        <seriesInfo name="BCP" value="14"/>
        <seriesInfo name="RFC" value="2119"/>
        <seriesInfo name="DOI" value="10.17487/RFC2119"/>
      </reference>
      <reference anchor="RFC8174">
        <front>
          <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
          <author fullname="B. Leiba" initials="B." surname="Leiba"/>
          <date month="May" year="2017"/>
          <abstract>
            <t>RFC 2119 specifies common key words that may be used in protocol specifications. This document aims to reduce the ambiguity by clarifying that only UPPERCASE usage of the key words have the defined special meanings.</t>
          </abstract>
        </front>
        <seriesInfo name="BCP" value="14"/>
        <seriesInfo name="RFC" value="8174"/>
        <seriesInfo name="DOI" value="10.17487/RFC8174"/>
      </reference>
    </references>
    <?line 393?>

<section numbered="false" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>TODO acknowledge.</t>
    </section>
  </back>
  <!-- ##markdown-source:
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
hJyoSPRgjoDp6b3aU9Na6PMcNO8VZa9CdNZeVeDx/WWBgQ9C3VBM1EVeMyRJ
GNvFPOA8kMNkgaXozu13nhrq7NivpayCxVQp9/CXgbGPJWt25qVqMwKJVwsh
w6jmsuTQYLHop83YJVSSThZx23liT8lOp06oHk5Q3axLkbTITZ6YDYpVtVJH
JA8COSsLHb6HCkeNqRqqw9XmqLfHT7DGh4hGvycjROnrzigopMGKTBebTRr/
8Eh7C/CUxJJNPDSR8UAJf1LAI2LSePUQTyG5Z8b/0AhQt36q0MQgPnnyGstZ
XOjTLWcpH/2cJjMnnjV4pklNly8WAseeGCrUNZL3Qq420pQID8SyFemaQ3k2
E7sc59z+UdeUr0V4p3Q3Q++PGLF9FXpfUkvC8oee85NqC+6D4F9fWTIG3/ct
0Fcjee1gF4Wpu+JHKqk04kBNsSz165QYORFKnOnIIUHp8GGRbGTfBSCSuL60
Ps6nKoqGskfHm8SRVlFZNonYkFvpxBElTp+VyhVNRc8jVzQ+q29eiJjxGaVH
h1CilbUFWm3OzE6bctrblcZElnKzppyYkrOmCXR21jRDmp41waT5mRSXMMsJ
Hal9WGs+ellBokWqM1OEdTZp/pokeJwTseS+12leX5V863KooqhGP+w/S1Oh
m1wmbX8mJjWz6ZJX6+L2lo+X7u4SZt9flftl7690d/YqU/FVSwwCf5zjDfEi
6ajpvTZz6SJvmelVmUUugNTUN0nGnJNfW0DX9yTisMcVcXgjqbRPOt5Z5QPP
jKn0oa4jddgJ0Y9D7Va6p8DT2xvcllbBOK23nSBpvCe9hoxVGijtuvNCUT/D
GH5GYCPPvffzM2TJAvyEz8/Gz+aSn8fLAlZ8sEy50doRafenzE1RMp1CJrDT
D14UXq5lKdy6aFhmd60lW+tXDcv88l8wYgnb/eV/FlYptCq02OoW2hB668KC
N7TgA/lhKm6YCr1Q2Rwzs6P63fHg8o7N5sObyHIbjz4r0dugsrkdZPlsq3fY
7r9a5C32Pnr+1KON+6oaYl6f7LYKRVOrBj5RSxGYKsqsR3kP6hpQEQp0TrN+
Dyxpv6SaZoQc9D2gL7ceMHEGujz1c5xWki4uK6I54svtGrgl6M5JAQC11sh2
uCH6iFiUJvGJQ+xlRz/SpfwIaSAB6yMz6frekFKVJFrymUYeg843x+0HhMJC
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
lb3b4g0OEVLjyjkk9rG/A3y0raJmQAAA

-->

</rfc>
