<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="rfc2629.xslt" ?>
<!-- generated by https://github.com/cabo/kramdown-rfc2629 version 1.2.9 -->
<?rfc toc="yes"?>
<?rfc tocindent="yes"?>
<?rfc sortrefs="yes"?>
<?rfc symrefs="yes"?>
<?rfc strict="yes"?>
<?rfc compact="yes"?>
<?rfc comments="yes"?>
<?rfc inline="yes"?>
<?rfc-ext html-pretty-print="prettyprint https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"?>
<rfc xmlns:x="http://purl.org/net/xml2rfc/ext"
     category="std"
     docName="draft-ietf-httpbis-variants-05"
     ipr="trust200902"
     submissionType="IETF"
     updates="7234">
   <x:feedback template="mailto:ietf-http-wg@w3.org?subject={docname},%20%22{section}%22\&amp;amp;body=%3c{ref}%3e:"/>
   <front>
      <title>HTTP Representation Variants</title>
      <author fullname="Mark Nottingham" initials="M." surname="Nottingham">
         <organization>Fastly</organization>
         <address>
            <email>mnot@mnot.net</email>
            <uri>https://www.mnot.net/</uri>
         </address>
      </author>
      <date year="2019" month="March" day="25"/>
      <area>Applications and Real-Time</area>
      <workgroup>HTTP</workgroup>
      <keyword>content negotiation</keyword>
      <abstract>
         <t>This specification introduces an alternative way to communicate a secondary cache key for a HTTP resource, using the HTTP “Variants” and “Variant-Key” response header fields. Its aim is to make HTTP proactive content negotiation more cache-friendly.</t>
      </abstract>
      <note title="Note to Readers">
         <t>
            <spanx>RFC EDITOR: please remove this section before publication</spanx>
         </t>
         <t>Discussion of this draft takes place on the HTTP working group mailing list (ietf-http-wg@w3.org), which is archived at <eref target="https://lists.w3.org/Archives/Public/ietf-http-wg/">https://lists.w3.org/Archives/Public/ietf-http-wg/</eref>.</t>
         <t>Working Group information can be found at <eref target="https://httpwg.github.io/">https://httpwg.github.io/</eref>; source code and issues list for this draft can be found at <eref target="https://github.com/httpwg/http-extensions/labels/variants">https://github.com/httpwg/http-extensions/labels/variants</eref>.</t>
         <t>There is a prototype implementation of the algorithms herein at <eref target="https://github.com/mnot/variants-toy">https://github.com/mnot/variants-toy</eref>.</t>
      </note>
   </front>
   <middle>
      <section anchor="introduction" title="Introduction">
         <t>HTTP proactive content negotiation (<xref target="RFC7231" x:fmt="," x:sec="3.4.1"/>) is seeing renewed interest, both for existing request headers like Accept-Language and for newer ones (for example, see <xref target="I-D.ietf-httpbis-client-hints"/>).</t>
         <t>Successfully reusing negotiated responses that have been stored in a HTTP cache requires establishment of a secondary cache key (<xref target="RFC7234" x:fmt="," x:sec="4.1"/>). Currently, the Vary header (<xref target="RFC7231" x:fmt="," x:sec="7.1.4"/>) does this by nominating a set of request headers.</t>
         <t>HTTP’s caching model allows a certain amount of latitude in normalising those request header field values, so as to increase the chances of a cache hit while still respecting the semantics of that header. However, normalisation is not formally defined, leading to divergence in cache behaviours.</t>
         <t>Even when the headers’ semantics are understood, a cache does not know enough about the possible alternative representations available on the origin server to make an appropriate decision.</t>
         <t>For example, if a cache has stored the following request/response pair:</t>
         <figure>
            <artwork type="example">
GET /foo HTTP/1.1
Host: www.example.com
Accept-Language: en;q=0.5, fr;q=1.0

</artwork>
         </figure>
         <figure>
            <artwork type="example">
HTTP/1.1 200 OK
Content-Type: text/html
Content-Language: en
Vary: Accept-Language
Transfer-Encoding: chunked

[English content]
</artwork>
         </figure>
         <t>Provided that the cache has full knowledge of the semantics of Accept-Language and Content-Language, it will know that an English representation is available and might be able to infer that a French representation is not available. But, it does not know (for example) whether a Japanese representation is available without making another request, incurring possibly unnecessary latency.</t>
         <t>This specification introduces the HTTP Variants response header field (<xref target="variants"/>) to enumerate the available variant representations on the origin server, to provide clients and caches with enough information to properly satisfy requests – either by selecting a response from cache or by forwarding the request towards the origin – by following the algorithm defined in <xref target="cache"/>.</t>
         <t>Its companion Variant-Key response header field (<xref target="variant-key"/>) indicates the applicable key(s) that the response is associated with, so that it can be reliably reused in the future. When this specification is in use, the example above might become:</t>
         <figure>
            <artwork type="example">
GET /foo HTTP/1.1
Host: www.example.com
Accept-Language: en;q=0.5, fr;q=1.0

</artwork>
         </figure>
         <figure>
            <artwork type="example">
HTTP/1.1 200 OK
Content-Type: text/html
Content-Language: en
Vary: Accept-Language
Variants: Accept-Language;de;en;jp
Variant-Key: en
Transfer-Encoding: chunked

[English content]
</artwork>
         </figure>
         <t>Proactive content negotiation mechanisms that wish to be used with Variants need to define how to do so explicitly; see <xref target="define"/>. As a result, it is best suited for negotiation over request headers that are well-understood.</t>
         <t>Variants also works best when content negotiation takes place over a constrained set of representations; since each variant needs to be listed in the header field, it is ill-suited for open-ended sets of representations.</t>
         <t>Variants can be seen as a simpler version of the Alternates header field introduced by <xref target="RFC2295"/>; unlike that mechanism, Variants does not require specification of each combination of attributes, and does not assume that each combination has a unique URL.</t>
         <section anchor="notational-conventions" title="Notational Conventions">
            <t>The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” 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>
            <t>This specification uses the Augmented Backus-Naur Form (ABNF) notation of <xref target="RFC5234"/> but relies on Structured Headers from <xref target="I-D.ietf-httpbis-header-structure"/> for parsing.</t>
            <t>Additionally, it uses the “field-name” rule from <xref target="RFC7230"/>, and “type”, “subtype”, “content-coding” and “language-range” from <xref target="RFC7231"/>.</t>
         </section>
      </section>
      <section anchor="variants" title="The “Variants” HTTP Header Field">
         <t>The Variants HTTP response header field indicates what representations are available for a given resource at the time that the response is produced, by enumerating the request header fields that it varies on, along with the values that are available for each.</t>
         <t>Variants is a Structured Header <xref target="I-D.ietf-httpbis-header-structure"/>. Its value MUST be a list-of-lists (<xref target="I-D.ietf-httpbis-header-structure" x:fmt="of" x:sec="3.3"/>) whose members are strings (<xref target="I-D.ietf-httpbis-header-structure" x:fmt="of" x:sec="3.8"/>) or tokens (<xref target="I-D.ietf-httpbis-header-structure" x:fmt="of" x:sec="3.9"/>). Its ABNF is:</t>
         <figure>
            <artwork type="abnf">
Variants        = sh-list-of-lists
</artwork>
         </figure>
         <t>If Structured Header parsing fails or a list-member has the wrong type, the client MUST treat the representation as having no Variants header field.</t>
         <t>The Variants header field represents an ordered list of “variant-axes”, each of which consists of a request header “field-name” string and a list of “available-value” strings. Each inner-list in the Variants header field value is parsed into a variant-axis. The first list-member of the inner-list is interpreted as the field-name, and the remaining list-members are the available-values. Any list-member that is a token (<xref target="I-D.ietf-httpbis-header-structure" x:fmt="of" x:sec="3.9"/>) is interpreted as a string containing the same characters.</t>
         <t>Field-names in the Variants header field value MUST match the field-name production (<xref target="RFC7230" x:fmt="of" x:sec="3.2"/>). Clients receiving an invalid field-name MUST NOT match it to any content negotiating mechanism.</t>
         <t>So, given this example header field:</t>
         <figure>
            <artwork type="example">
Variants: Accept-Encoding;gzip
</artwork>
         </figure>
         <t>a recipient can infer that the only content-coding available for that resource is “gzip” (along with the “identity” non-encoding; see <xref target="content-encoding"/>).</t>
         <t>Given:</t>
         <figure>
            <artwork type="example">
Variants: accept-encoding
</artwork>
         </figure>
         <t>a recipient can infer that no content-codings (beyond identity) are supported. Note that as always, field-name is case-insensitive.</t>
         <t>A more complex example:</t>
         <figure>
            <artwork type="example">
Variants: Accept-Encoding;gzip;br, Accept-Language;en ;fr
</artwork>
         </figure>
         <t>Here, recipients can infer that two content-codings in addition to “identity” are available, as well as two content languages. Note that, as with all Structured Header lists, they might occur in the same header field or separately, like this:</t>
         <figure>
            <artwork type="example">
Variants: Accept-Encoding;gzip;brotli
Variants: Accept-Language;en ;fr
</artwork>
         </figure>
         <t>The ordering of available-values after the field-name is significant, as it might be used by the header’s algorithm for selecting a response (in this example, the first language is the default; see <xref target="content-language"/>).</t>
         <t>The ordering of the request header fields themselves indicates descending application of preferences; in the example above, a cache that has all of the possible permutations stored will honour the client’s preferences for Accept-Encoding before honouring Accept-Language.</t>
         <t>Origin servers SHOULD consistently send Variant header fields on all cacheable (as per <xref target="RFC7234" x:fmt="," x:sec="3"/>) responses for a resource, since its absence will trigger caches to fall back to Vary processing.</t>
         <t>Likewise, servers MUST send the Variant-Key response header field when sending Variants, since its absence means that the stored response will not be reused when this specification is implemented.</t>
         <t>
            <spanx>RFC EDITOR: Please remove the next paragraph before publication.</spanx>
         </t>
         <t>Implementations of drafts of this specification MUST implement an HTTP header field named “Variants-##” instead of the “Variants” header field specified by the final RFC, with “##” replaced by the draft number being implemented. For example, implementations of draft-ietf-httpbis-variants-05 would implement “Variants-05”.</t>
         <section anchor="vary" title="Relationship to Vary">
            <t>This specification updates <xref target="RFC7234"/> to allow caches that implement it to ignore request header fields in the Vary header for the purposes of secondary cache key calculation (<xref target="RFC7234" x:fmt="," x:sec="4.1"/>) when their semantics are implemented as per this specification and their corresponding response header field is listed in Variants.</t>
            <t>If any member of the Vary header does not have a corresponding variant that is understood by the implementation, it is still subject to the requirements there.</t>
            <t>See <xref target="partial"/> for an example.</t>
            <t>In practice, implementation of Vary varies considerably. As a result, cache efficiency might drop considerably when Variants does not contain all of the headers referenced by Vary, because some implementations might choose to disable Variants processing when this is the case.</t>
         </section>
      </section>
      <section anchor="variant-key" title="The “Variant-Key” HTTP Header Field">
         <t>The Variant-Key HTTP response header field identifies a set of variants provided by the representation it occurs within. A variant is identified by a selection of one available-value from each variant-axis from the Variants header field.</t>
         <t>Variant-Key is a Structured Header <xref target="I-D.ietf-httpbis-header-structure"/>. Its value MUST be a list-of-lists (<xref target="I-D.ietf-httpbis-header-structure" x:fmt="of" x:sec="3.3"/>) whose members are strings (<xref target="I-D.ietf-httpbis-header-structure" x:fmt="of" x:sec="3.8"/>) or tokens (<xref target="I-D.ietf-httpbis-header-structure" x:fmt="of" x:sec="3.9"/>). Its ABNF is:</t>
         <figure>
            <artwork type="abnf">
Variant-Key      = sh-list-of-lists
</artwork>
         </figure>
         <t>If Structured Header parsing fails or a list-member has the wrong type, the client MUST treat the representation as having no Variant-Key header field.</t>
         <t>Each inner-list MUST have the same number of list-members as there are variant-axes in the representation’s Variants header field. If not, the client MUST treat the representation as having no Variant-Key header field.</t>
         <t>Each list-member is treated as identifying an available-value for the corresponding variant-axis’ field-name. Any list-member that is a token (<xref target="I-D.ietf-httpbis-header-structure" x:fmt="of" x:sec="3.9"/>) is interpreted as a string containing the same characters. These available-values do not need to explicitly appear in the Variants header field. For example, Accept-Encoding defines an implicit “identity” available-value (<xref target="content-encoding"/>).</t>
         <t>For example:</t>
         <figure>
            <artwork type="example">
Variants: Accept-Encoding;gzip;br, Accept-Language;en ;fr
Variant-Key: gzip;fr
</artwork>
         </figure>
         <t>This header pair indicates that the representation has a “gzip” content-coding and “fr” content-language.</t>
         <t>If the response can be used to satisfy more than one request, they can be listed in additional members. For example:</t>
         <figure>
            <artwork type="example">
Variants: Accept-Encoding;gzip;br, Accept-Language;en ;fr
Variant-Key: gzip;fr, "identity";fr
</artwork>
         </figure>
         <t>indicates that this response can be used for requests whose Accept-Encoding algorithm selects “gzip” or “identity”, as long as the Accept-Language algorithm selects “fr” – perhaps because there is no gzip-compressed French representation.</t>
         <t>When more than one Variant-Key value is in a response, the first one present MUST correspond to the request that caused that response to be generated.</t>
         <t>Parsing is strict. For example:</t>
         <figure>
            <artwork type="example">
Variants: Accept-Encoding;gzip;br, Accept-Language;en ;fr
Variant-Key: gzip;fr, identity;fr, br;fr;oops
</artwork>
         </figure>
         <t>is treated as if the Variant-Key header were completely absent, which will tend to disable caching for the representation that contains it.</t>
         <t>Note that in</t>
         <figure>
            <artwork type="example">
Variant-Key: gzip ;fr
Variant-Key: "gzip ";fr
</artwork>
         </figure>
         <t>The whitespace after “gzip” in the first header field value is excluded by the token parsing algorithm, but the whitespace in the second header field value is included by the string parsing algorithm. This will likely cause the second header field value to fail to match client requests.</t>
         <t>
            <spanx>RFC EDITOR: Please remove the next paragraph before publication.</spanx>
         </t>
         <t>Implementations of drafts of this specification MUST implement an HTTP header field named “Variant-Key-##” instead of the “Variant-Key” header field specified by the final RFC, with “##” replaced by the draft number being implemented. For example, implementations of draft-ietf-httpbis-variants-05 would implement “Variant-Key-05”.</t>
      </section>
      <section anchor="cache" title="Cache Behaviour">
         <t>Caches that implement the Variants header field and the relevant semantics of the field-names it contains can use that knowledge to either select an appropriate stored representation, or forward the request if no appropriate representation is stored.</t>
         <t>They do so by running this algorithm (or its functional equivalent) upon receiving a request:</t>
         <t>Given incoming-request (a mapping of field-names to lists of field values), and stored-responses (a list of stored responses suitable for reuse as defined in <xref target="RFC7234" x:fmt="of" x:sec="4"/>, excepting the requirement to calculate a secondary cache key):</t>
         <t>
            <list style="numbers">
               <t>If stored-responses is empty, return an empty list.</t>
               <t>Order stored-responses by the “Date” header field, most recent to least recent.</t>
               <t>Let sorted-variants be an empty list.</t>
               <t>If the freshest member of stored-responses (as per <xref target="RFC7234" x:fmt="," x:sec="4.2"/>) has one or more “Variants” header field(s) that successfully parse according to <xref target="variants"/>: <list style="numbers">
                     <t>Select one member of stored-responses with a “Variants” header field-value(s) that successfully parses according to <xref target="variants"/> and let variants-header be this parsed value. This SHOULD be the most recent response, but MAY be from an older one as long as it is still fresh.</t>
                     <t>For each variant-axis in variants-header: <list style="numbers">
                           <t>If variant-axis’ field-name corresponds to the request header field identified by a content negotiation mechanism that the implementation supports: <list style="numbers">
                                 <t>Let request-value be the field-value associated with field-name in incoming-request (after being combined as allowed by <xref target="RFC7230" x:fmt="of" x:sec="3.2.2"/>), or null if field-name is not in incoming-request.</t>
                                 <t>Let sorted-values be the result of running the algorithm defined by the content negotiation mechanism with request-value and variant-axis’ available-values.</t>
                                 <t>Append sorted-values to sorted-variants.</t>
                              </list>
                           </t>
                        </list> At this point, sorted-variants will be a list of lists, each member of the top-level list corresponding to a variant-axis in the Variants header field-value, containing zero or more items indicating available-values that are acceptable to the client, in order of preference, greatest to least.</t>
                  </list>
               </t>
               <t>Return result of running Compute Possible Keys (<xref target="find"/>) on sorted-variants, an empty list and an empty list.</t>
            </list>
         </t>
         <t>This returns a list of lists of strings suitable for comparing to the parsed Variant-Keys (<xref target="variant-key"/>) that represent possible responses on the server that can be used to satisfy the request, in preference order, provided that their secondary cache key (after removing the headers covered by Variants) matches. <xref target="check_vary"/> illustrates one way to do this.</t>
         <section anchor="find" title="Compute Possible Keys">
            <t>This algorithm computes the cross-product of the elements of key-facets.</t>
            <t>Given key-facets (a list of lists of strings), and key-stub (a list of strings representing a partial key), and possible-keys (a list of lists of strings):</t>
            <t>
               <list style="numbers">
                  <t>Let values be the first member of key-facets.</t>
                  <t>Let remaining-facets be a copy of all of the members of key-facets except the first.</t>
                  <t>For each value in values: <list style="numbers">
                        <t>Let this-key be a copy of key-stub.</t>
                        <t>Append value to this-key.</t>
                        <t>If remaining-facets is empty, append this-key to possible-keys.</t>
                        <t>Otherwise, run Compute Possible Keys on remaining-facets, this-key and possible-keys.</t>
                     </list>
                  </t>
                  <t>Return possible-keys.</t>
               </list>
            </t>
         </section>
         <section anchor="check_vary" title="Check Vary">
            <t>This algorithm is an example of how an implementation can meet the requirement to apply the members of the Vary header field that are not covered by Variants.</t>
            <t>Given stored-response (a stored response):</t>
            <t>
               <list style="numbers">
                  <t>Let filtered-vary be the field-value(s) of stored-response’s “Vary” header field.</t>
                  <t>Let processed-variants be a list containing the request header fields that identify the content negotiation mechanisms supported by the implementation.</t>
                  <t>Remove any member of filtered-vary that is a case-insensitive match for a member of processed-variants.</t>
                  <t>If the secondary cache key (as calculated in <xref target="RFC7234" x:fmt="," x:sec="4.1"/>) for stored_response matches incoming-request, using filtered-vary for the value of the “Vary” response header, return True.</t>
                  <t>Return False.</t>
               </list>
            </t>
            <t>This returns a Boolean that indicates whether stored-response can be used to satisfy the request.</t>
            <t>Note that implementation of the Vary header field varies in practice, and the algorithm above illustrates only one way to apply it. It is equally viable to forward the request if there is a request header listed in Vary but not Variants.</t>
         </section>
         <section anchor="example-of-cache-behaviour" title="Example of Cache Behaviour">
            <t>For example, if the selected variants-header was:</t>
            <figure>
               <artwork type="example">
Variants: Accept-Language;en;fr;de, Accept-Encoding;gzip;br
</artwork>
            </figure>
            <t>and the request contained the headers:</t>
            <figure>
               <artwork type="example">
Accept-Language: fr;q=1.0, en;q=0.1
Accept-Encoding: gzip
</artwork>
            </figure>
            <t>Then the sorted-variants would be:</t>
            <figure>
               <artwork type="example">
[
  ["fr", "en"]         // prefers French, will accept English
  ["gzip", "identity"] // prefers gzip encoding, will accept identity
]
</artwork>
            </figure>
            <t>Which means that the result of the <xref format="title" target="cache"/> algorithm would be:</t>
            <figure>
               <artwork type="example">
[
  ["fr", "gzip"],
  ["fr", "identity"],
  ["en", "gzip"],
  ["en", "identity"]
]
</artwork>
            </figure>
            <t>Representing a first preference of a French, gzip’d response. Thus, if a cache has a response with:</t>
            <figure>
               <artwork type="example">
Variant-Key: fr; gzip
</artwork>
            </figure>
            <t>it could be used to satisfy the first preference. If not, responses corresponding to the other keys could be returned, or the request could be forwarded towards the origin.</t>
            <section anchor="a-variant-missing-from-the-cache"
                     title="A Variant Missing From the Cache">
               <t>If the selected variants-header was:</t>
               <figure>
                  <artwork type="example">
Variants: Accept-Language;en;fr;de
</artwork>
               </figure>
               <t>And a request comes in with the following headers:</t>
               <figure>
                  <artwork type="example">
Accept-Language: de;q=1.0, es;q=0.8
</artwork>
               </figure>
               <t>Then sorted-variants in <xref format="title" target="cache"/> is:</t>
               <figure>
                  <artwork type="example">
[
  ["de"]         // prefers German; will not accept English
]
</artwork>
               </figure>
               <t>If the cache contains responses with the following Variant-Keys:</t>
               <figure>
                  <artwork type="example">
Variant-Key: fr
Variant-Key: en
</artwork>
               </figure>
               <t>Then the cache needs to forward the request to the origin server, since Variants indicates that “de” is available, and that is acceptable to the client.</t>
            </section>
            <section anchor="variants-that-dont-overlap-the-clients-request"
                     title="Variants That Don’t Overlap the Client’s Request">
               <t>If the selected variants-header was:</t>
               <figure>
                  <artwork type="example">
Variants: Accept-Language;en;fr;de
</artwork>
               </figure>
               <t>And a request comes in with the following headers:</t>
               <figure>
                  <artwork type="example">
Accept-Language: es;q=1.0, ja;q=0.8
</artwork>
               </figure>
               <t>Then sorted-variants in <xref format="title" target="cache"/> are:</t>
               <figure>
                  <artwork type="example">
[
  ["en"]
]
</artwork>
               </figure>
               <t>This allows the cache to return a “Variant-Key: en” response even though it’s not in the set the client prefers.</t>
            </section>
         </section>
      </section>
      <section anchor="origin" title="Origin Server Behaviour">
         <t>Origin servers that wish to take advantage of Variants will need to generate both the Variants (<xref target="variants"/>) and Variant-Key (<xref target="variant-key"/>) header fields in all cacheable responses for a given resource. If either is omitted and the response is stored, it will have the effect of disabling caching for that resource until it is no longer stored (e.g., it expires, or is evicted).</t>
         <t>Likewise, origin servers will need to assure that the members of both header field values are in the same order and have the same length, since discrepancies will cause caches to avoid using the responses they occur in.</t>
         <t>The value of the Variants header should be relatively stable for a given resource over time; when it changes, it can have the effect of invalidating previously stored responses.</t>
         <t>As per <xref target="vary"/>, the Vary header is required to be set appropriately when Variants is in use, so that caches that do not implement this specification still operate correctly.</t>
         <t>Origin servers are advised to carefully consider which content negotiation mechanisms to enumerate in Variants; if a mechanism is not supported by a receiving cache, it will “downgrade” to Vary handling, which can negatively impact cache efficiency.</t>
         <section anchor="examples" title="Examples">
            <t>The operation of Variants is illustrated by the examples below.</t>
            <section anchor="single-variant" title="Single Variant">
               <t>Given a request/response pair:</t>
               <figure>
                  <artwork type="example">
GET /clancy HTTP/1.1
Host: www.example.com
Accept-Language: en;q=1.0, fr;q=0.5

</artwork>
               </figure>
               <figure>
                  <artwork type="example">
HTTP/1.1 200 OK
Content-Type: image/gif
Content-Language: en
Cache-Control: max-age=3600
Variants: Accept-Language;en;de
Variant-Key: en
Vary: Accept-Language
Transfer-Encoding: chunked
</artwork>
               </figure>
               <t>Upon receipt of this response, the cache knows that two representations of this resource are available, one with a language of “en”, and another whose language is “de”.</t>
               <t>Subsequent requests (while this response is fresh) will cause the cache to either reuse this response or forward the request, depending on what the selection algorithm determines.</t>
               <t>So, if a request with “en” in Accept-Language is received and its q-value indicates that it is acceptable, the stored response is used. A request that indicates that “de” is acceptable will be forwarded to the origin, thereby populating the cache. A cache receiving a request that indicates both languages are acceptable will use the q-value to make a determination of what response to return.</t>
               <t>A cache receiving a request that does not list either language as acceptable (or does not contain an Accept-Language at all) will return the “en” representation (possibly fetching it from the origin), since it is listed first in the Variants list.</t>
               <t>Note that Accept-Language is listed in Vary, to assure backwards-compatibility with caches that do not support Variants.</t>
            </section>
            <section anchor="multiple-variants" title="Multiple Variants">
               <t>A more complicated request/response pair:</t>
               <figure>
                  <artwork type="example">
GET /murray HTTP/1.1
Host: www.example.net
Accept-Language: en;q=1.0, fr;q=0.5
Accept-Encoding: gzip, br

</artwork>
               </figure>
               <figure>
                  <artwork type="example">
HTTP/1.1 200 OK
Content-Type: image/gif
Content-Language: en
Content-Encoding: br
Variants: Accept-Language;en;jp;de
Variants: Accept-Encoding;br;gzip
Variant-Key: en;br
Vary: Accept-Language, Accept-Encoding
Transfer-Encoding: chunked
</artwork>
               </figure>
               <t>Here, the cache knows that there are two axes that the response varies upon; language and encoding. Thus, there are a total of nine possible representations for the resource (including the identity encoding), and the cache needs to consider the selection algorithms for both axes.</t>
               <t>Upon a subsequent request, if both selection algorithms return a stored representation, it can be served from cache; otherwise, the request will need to be forwarded to origin.</t>
            </section>
            <section anchor="partial" title="Partial Coverage">
               <t>Now, consider the previous example, but where only one of the Vary’d axes (encoding) is listed in Variants:</t>
               <figure>
                  <artwork type="example">
GET /bar HTTP/1.1
Host: www.example.net
Accept-Language: en;q=1.0, fr;q=0.5
Accept-Encoding: gzip, br

</artwork>
               </figure>
               <figure>
                  <artwork type="example">
HTTP/1.1 200 OK
Content-Type: image/gif
Content-Language: en
Content-Encoding: br
Variants: Accept-Encoding;br;gzip
Variant-Key: br
Vary: Accept-Language, Accept-Encoding
Transfer-Encoding: chunked
</artwork>
               </figure>
               <t>Here, the cache will need to calculate a secondary cache key as per <xref target="RFC7234" x:fmt="," x:sec="4.1"/> – but considering only Accept-Language to be in its field-value – and then continue processing Variants for the set of stored responses that the algorithm described there selects.</t>
            </section>
         </section>
      </section>
      <section anchor="define" title="Defining Content Negotiation Using Variants">
         <t>To be usable with Variants, proactive content negotiation mechanisms need to be specified to take advantage of it. Specifically, they:</t>
         <t>
            <list style="symbols">
               <t>MUST define a request header field that advertises the clients preferences or capabilities, whose field-name SHOULD begin with “Accept-“.</t>
               <t>MUST define the syntax of an available-value that will occur in Variants and Variant-Key.</t>
               <t>MUST define an algorithm for selecting a result. It MUST return a list of available-values that are suitable for the request, in order of preference, given the value of the request header nominated above (or null if the request header is absent) and an available-values list from the Variants header. If the result is an empty list, it implies that the cache cannot satisfy the request.</t>
            </list>
         </t>
         <t>
            <xref target="backports"/> fulfils these requirements for some existing proactive content negotiation mechanisms in HTTP.</t>
      </section>
      <section anchor="iana-considerations" title="IANA Considerations">
         <t>This specification registers the following entry in the Permanent Message Header Field Names registry established by <xref target="RFC3864"/>:</t>
         <t>
            <list style="symbols">
               <t>Header field name: Variants</t>
               <t>Applicable protocol: http</t>
               <t>Status: standard</t>
               <t>Author/Change Controller: IETF</t>
               <t>Specification document(s): [this document]</t>
               <t>Related information:</t>
            </list>
         </t>
         <t>This specification registers the following entry in the Permanent Message Header Field Names registry established by <xref target="RFC3864"/>:</t>
         <t>
            <list style="symbols">
               <t>Header field name: Variant-Key</t>
               <t>Applicable protocol: http</t>
               <t>Status: standard</t>
               <t>Author/Change Controller: IETF</t>
               <t>Specification document(s): [this document]</t>
               <t>Related information:</t>
            </list>
         </t>
      </section>
      <section anchor="security-considerations" title="Security Considerations">
         <t>If the number or advertised characteristics of the representations available for a resource are considered sensitive, the Variants header by its nature will leak them.</t>
         <t>Note that the Variants header is not a commitment to make representations of a certain nature available; the runtime behaviour of the server always overrides hints like Variants.</t>
      </section>
   </middle>
   <back>
      <references title="Normative References">
         <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"/>
            </front>
            <seriesInfo name="BCP" value="14"/>
            <seriesInfo name="RFC" value="2119"/>
            <seriesInfo name="DOI" value="10.17487/RFC2119"/>
         </reference>
         <reference anchor="RFC7231">
            <front>
               <title>Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content</title>
               <author fullname="R. Fielding"
                       initials="R."
                       role="editor"
                       surname="Fielding"/>
               <author fullname="J. Reschke"
                       initials="J."
                       role="editor"
                       surname="Reschke"/>
               <date month="June" year="2014"/>
            </front>
            <seriesInfo name="RFC" value="7231"/>
            <seriesInfo name="DOI" value="10.17487/RFC7231"/>
         </reference>
         <reference anchor="RFC7234">
            <front>
               <title>Hypertext Transfer Protocol (HTTP/1.1): Caching</title>
               <author fullname="R. Fielding"
                       initials="R."
                       role="editor"
                       surname="Fielding"/>
               <author fullname="M. Nottingham"
                       initials="M."
                       role="editor"
                       surname="Nottingham"/>
               <author fullname="J. Reschke"
                       initials="J."
                       role="editor"
                       surname="Reschke"/>
               <date month="June" year="2014"/>
            </front>
            <seriesInfo name="RFC" value="7234"/>
            <seriesInfo name="DOI" value="10.17487/RFC7234"/>
         </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"/>
            </front>
            <seriesInfo name="BCP" value="14"/>
            <seriesInfo name="RFC" value="8174"/>
            <seriesInfo name="DOI" value="10.17487/RFC8174"/>
         </reference>
         <reference anchor="RFC5234">
            <front>
               <title>Augmented BNF for Syntax Specifications: ABNF</title>
               <author fullname="D. Crocker"
                       initials="D."
                       role="editor"
                       surname="Crocker"/>
               <author fullname="P. Overell" initials="P." surname="Overell"/>
               <date month="January" year="2008"/>
            </front>
            <seriesInfo name="STD" value="68"/>
            <seriesInfo name="RFC" value="5234"/>
            <seriesInfo name="DOI" value="10.17487/RFC5234"/>
         </reference>
         <reference anchor="I-D.ietf-httpbis-header-structure">
            <front>
               <title>Structured Headers for HTTP</title>
               <author fullname="Mark Nottingham" initials="M" surname="Nottingham"/>
               <author fullname="Poul-Henning Kamp" initials="P" surname="Kamp"/>
               <date day="1" month="December" year="2018"/>
            </front>
            <seriesInfo name="Internet-Draft" value="draft-ietf-httpbis-header-structure-09"/>
         </reference>
         <reference anchor="RFC7230">
            <front>
               <title>Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing</title>
               <author fullname="R. Fielding"
                       initials="R."
                       role="editor"
                       surname="Fielding"/>
               <author fullname="J. Reschke"
                       initials="J."
                       role="editor"
                       surname="Reschke"/>
               <date month="June" year="2014"/>
            </front>
            <seriesInfo name="RFC" value="7230"/>
            <seriesInfo name="DOI" value="10.17487/RFC7230"/>
         </reference>
         <reference anchor="RFC4647">
            <front>
               <title>Matching of Language Tags</title>
               <author fullname="A. Phillips" initials="A." surname="Phillips"/>
               <author fullname="M. Davis" initials="M." surname="Davis"/>
               <date month="September" year="2006"/>
            </front>
            <seriesInfo name="BCP" value="47"/>
            <seriesInfo name="RFC" value="4647"/>
            <seriesInfo name="DOI" value="10.17487/RFC4647"/>
         </reference>
      </references>
      <references title="Informative References">
         <reference anchor="I-D.ietf-httpbis-client-hints">
            <front>
               <title>HTTP Client Hints</title>
               <author fullname="Ilya Grigorik" initials="I" surname="Grigorik"/>
               <date day="16" month="July" year="2018"/>
            </front>
            <seriesInfo name="Internet-Draft" value="draft-ietf-httpbis-client-hints-06"/>
         </reference>
         <reference anchor="RFC2295">
            <front>
               <title>Transparent Content Negotiation in HTTP</title>
               <author fullname="K. Holtman" initials="K." surname="Holtman"/>
               <author fullname="A. Mutz" initials="A." surname="Mutz"/>
               <date month="March" year="1998"/>
            </front>
            <seriesInfo name="RFC" value="2295"/>
            <seriesInfo name="DOI" value="10.17487/RFC2295"/>
         </reference>
         <reference anchor="RFC3864">
            <front>
               <title>Registration Procedures for Message Header Fields</title>
               <author fullname="G. Klyne" initials="G." surname="Klyne"/>
               <author fullname="M. Nottingham" initials="M." surname="Nottingham"/>
               <author fullname="J. Mogul" initials="J." surname="Mogul"/>
               <date month="September" year="2004"/>
            </front>
            <seriesInfo name="BCP" value="90"/>
            <seriesInfo name="RFC" value="3864"/>
            <seriesInfo name="DOI" value="10.17487/RFC3864"/>
         </reference>
      </references>
      <section anchor="backports"
               title="Variants for Existing Content Negotiation Mechanisms">
         <t>This appendix defines the required information to use existing proactive content negotiation mechanisms (as defined in <xref target="RFC7231" x:fmt="," x:sec="5.3"/>) with the Variants header field.</t>
         <section anchor="content-type" title="Accept">
            <t>This section defines variant handling for the Accept request header (section 5.3.2 of <xref target="RFC7231"/>).</t>
            <t>The syntax of an available-value for Accept is:</t>
            <figure>
               <artwork type="abnf">
accept-available-value = type "/" subtype
</artwork>
            </figure>
            <t>To perform content negotiation for Accept given a request-value and available-values:</t>
            <t>
               <list style="numbers">
                  <t>Let preferred-available be an empty list.</t>
                  <t>Let preferred-types be a list of the types in the request-value (or the empty list if request-value is null), ordered by their weight, highest to lowest, as per <xref target="RFC7231" x:fmt="of" x:sec="5.3.2"/> (omitting any coding with a weight of 0). If a type lacks an explicit weight, an implementation MAY assign one.</t>
                  <t>For each preferred-type in preferred-types: <list style="numbers">
                        <t>If any member of available-values matches preferred-type, using the media-range matching mechanism specified in <xref target="RFC7231" x:fmt="of" x:sec="5.3.2"/> (which is case-insensitive), append those members of available-values to preferred-available (preserving the precedence order implied by the media ranges’ specificity).</t>
                     </list>
                  </t>
                  <t>If preferred-available is empty, append the first member of available-values to preferred-available. This makes the first available-value the default when none of the client’s preferences are available.</t>
                  <t>Return preferred-available.</t>
               </list>
            </t>
            <t>Note that this algorithm explicitly ignores extension parameters on media types (e.g., “charset”).</t>
         </section>
         <section anchor="content-encoding" title="Accept-Encoding">
            <t>This section defines variant handling for the Accept-Encoding request header (section 5.3.4 of <xref target="RFC7231"/>).</t>
            <t>The syntax of an available-value for Accept-Encoding is:</t>
            <figure>
               <artwork type="abnf">
accept-encoding-available-value = content-coding / "identity"
</artwork>
            </figure>
            <t>To perform content negotiation for Accept-Encoding given a request-value and available-values:</t>
            <t>
               <list style="numbers">
                  <t>Let preferred-available be an empty list.</t>
                  <t>Let preferred-codings be a list of the codings in the request-value (or the empty list if request-value is null), ordered by their weight, highest to lowest, as per <xref target="RFC7231" x:fmt="of" x:sec="5.3.1"/> (omitting any coding with a weight of 0). If a coding lacks an explicit weight, an implementation MAY assign one.</t>
                  <t>If “identity” is not a member of preferred-codings, append “identity”.</t>
                  <t>Append “identity” to available-values.</t>
                  <t>For each preferred-coding in preferred-codings: <list style="numbers">
                        <t>If there is a case-insensitive, character-for-character match for preferred-coding in available-values, append that member of available-values to preferred-available.</t>
                     </list>
                  </t>
                  <t>Return preferred-available.</t>
               </list>
            </t>
            <t>Note that the unencoded variant needs to have a Variant-Key header field with a value of “identity” (as defined in <xref target="RFC7231" x:fmt="of" x:sec="5.3.4"/>).</t>
         </section>
         <section anchor="content-language" title="Accept-Language">
            <t>This section defines variant handling for the Accept-Language request header (section 5.3.5 of <xref target="RFC7231"/>).</t>
            <t>The syntax of an available-value for Accept-Language is:</t>
            <figure>
               <artwork type="abnf">
accept-encoding-available-value = language-range
</artwork>
            </figure>
            <t>To perform content negotiation for Accept-Language given a request-value and available-values:</t>
            <t>
               <list style="numbers">
                  <t>Let preferred-available be an empty list.</t>
                  <t>Let preferred-langs be a list of the language-ranges in the request-value (or the empty list if request-value is null), ordered by their weight, highest to lowest, as per <xref target="RFC7231" x:fmt="of" x:sec="5.3.1"/> (omitting any language-range with a weight of 0). If a language-range lacks a weight, an implementation MAY assign one.</t>
                  <t>For each preferred-lang in preferred-langs: <list style="numbers">
                        <t>If any member of available-values matches preferred-lang, using either the Basic or Extended Filtering scheme defined in <xref target="RFC4647" x:fmt="of" x:sec="3.3"/>, append those members of available-values to preferred-available (preserving their order).</t>
                     </list>
                  </t>
                  <t>If preferred-available is empty, append the first member of available-values to preferred-available. This makes the first available-value the default when none of the client’s preferences are available.</t>
                  <t>Return preferred-available.</t>
               </list>
            </t>
         </section>
      </section>
      <section anchor="acknowledgements" numbered="false" title="Acknowledgements">
         <t>This protocol is conceptually similar to, but simpler than, Transparent Content Negotiation <xref target="RFC2295"/>. Thanks to its authors for their inspiration.</t>
         <t>It is also a generalisation of a Fastly VCL feature designed by Rogier ‘DocWilco’ Mulhuijzen.</t>
         <t>Thanks to Hooman Beheshti, Ilya Grigorik, Leif Hedstrom, and Jeffrey Yasskin for their review and input.</t>
      </section>
   </back>
</rfc>
