RFC 2295

Transparent Content Negotiation in HTTP

๐Ÿ“… published March 1998, from ietf

RFC 2295 ๐Ÿ’– Transparent Content Negotiation in HTTP

Okay bestie, the title block is giving main character energy ๐ŸŽ€. This is RFC 2295, called Transparent Content Negotiation in HTTP. K. Holtman (from TUE) and A. Mutz (from Hewlett-Packard) wrote it, and it came out in March 1998 in the Network Working Group. Its category is Experimental ๐Ÿงช. Nothing is listed as obsoleted or updated in its header, so it doesn't replace or change any earlier RFCs โœจ.

Status of this Memo ๐Ÿ“œ

This memo defines an Experimental Protocol for the Internet community. It does not specify an Internet standard of any kind. Discussion and suggestions for improvement are requested. Distribution of this memo is unlimited.

Copyright (C) The Internet Society (1998). All Rights Reserved.

Abstract ๐ŸŒธ

HTTP lets web site authors put multiple versions of the same information under a single URL. Transparent content negotiation is an extensible negotiation mechanism, layered on top of HTTP, that automatically picks the best version whenever the URL gets accessed. This is what makes it possible to smoothly roll out new web data formats and markup tags ๐Ÿš€.

Table of Contents ๐Ÿ—บ๏ธ

1  Introduction................................................4
 1.1 Background................................................4

2  Terminology.................................................5
 2.1 Terms from HTTP/1.1.......................................5
 2.2 New terms.................................................6

3  Notation....................................................8

4  Overview....................................................9
 4.1 Content negotiation.......................................9
 4.2 HTTP/1.0 style negotiation scheme.........................9
 4.3 Transparent content negotiation scheme...................10
 4.4 Optimizing the negotiation process.......................12
 4.5 Downwards compatibility with non-negotiating user agents.14
 4.6 Retrieving a variant by hand.............................15
 4.7 Dimensions of negotiation................................15
 4.8 Feature negotiation......................................15
 4.9 Length of variant lists..................................16
 4.10 Relation with other negotiation schemes.................16

5  Variant descriptions.......................................17
 5.1 Syntax...................................................17
 5.2 URI......................................................17
 5.3 Source-quality...........................................18
 5.4 Type, charset, language, and length......................19
 5.5 Features.................................................19
 5.6 Description..............................................19
 5.7 Extension-attribute......................................20

6  Feature negotiation........................................20
 6.1 Feature tags.............................................20
 6.1.1 Feature tag values.....................................21
 6.2 Feature sets.............................................21
 6.3 Feature predicates.......................................22
 6.4 Features attribute.......................................24

7  Remote variant selection algorithms........................25
 7.1 Version numbers..........................................25

8  Content negotiation status codes and headers...............25
 8.1 506 Variant Also Negotiates..............................25
 8.2 Accept-Features..........................................26
 8.3 Alternates...............................................27
 8.4 Negotiate................................................28
 8.5 TCN......................................................30
 8.6 Variant-Vary.............................................30

9  Cache validators...........................................31
 9.1 Variant list validators..................................31
 9.2 Structured entity tags...................................31
 9.3 Assigning entity tags to variants........................32

10 Content negotiation responses..............................32
 10.1 List response...........................................33
 10.2 Choice response.........................................34
 10.3 Adhoc response..........................................37
 10.4 Reusing the Alternates header...........................38
 10.5 Extracting a normal response from a choice response.....39
 10.6 Elaborate Vary headers..................................39
 10.6.1 Construction of an elaborate Vary header..............40
 10.6.2 Caching of an elaborate Vary header...................41
 10.7 Adding an Expires header for HTTP/1.0 compatibility.....41
 10.8 Negotiation on content encoding.........................41

11 User agent support for transparent negotiation.............42
 11.1 Handling of responses...................................42
 11.2 Presentation of a transparently negotiated resource.....42

12 Origin server support for transparent negotiation..........43
 12.1 Requirements............................................43
 12.2 Negotiation on transactions other than GET and HEAD.....45

13 Proxy support for transparent negotiation..................45

14 Security and privacy considerations........................46
 14.1 Accept- headers revealing personal information..........46
 14.2 Spoofing of responses from variant resources............47
 14.3 Security holes revealed by negotiation..................47

15 Internationalization considerations........................47

16 Acknowledgments............................................47

17 References.................................................48

18 Authors' Addresses.........................................48

19 Appendix: Example of a local variant selection algorithm...49
 19.1 Computing overall quality values........................49
 19.2 Determining the result..................................51
 19.3 Ranking dimensions......................................51

20 Appendix: feature negotiation examples.....................52
 20.1 Use of feature tags.....................................52
 20.2 Use of numeric feature tags.............................53
 20.3 Feature tag design......................................53

21 Appendix: origin server implementation considerations......54
 21.1 Implementation with a CGI script........................54
 21.2 Direct support by HTTP servers..........................55
 21.3 Web publishing tools....................................55

22 Appendix: Example of choice response construction..........55

23 Full Copyright Statement...................................58

1. Introduction โœจ

HTTP lets web site authors put multiple versions of the same information under a single URI. Each of these versions is called a variant ๐Ÿฆ‹. Transparent content negotiation is an extensible negotiation mechanism for automatically and efficiently retrieving the best variant whenever a GET or HEAD request comes in. It's how new web data formats and markup tags get smoothly deployed ๐Ÿ’….

This specification defines transparent content negotiation as an extension on top of the HTTP/1.1 protocol [1]. But using this extension doesn't require HTTP/1.1, okay? It can also work if some or all of the parties are HTTP/1.0 [2] systems ๐Ÿ™Œ.

It's called transparent because it makes all variants that exist inside the origin server visible to outside parties ๐Ÿ‘€.

Note: Some members of the IETF are working on a bunch of activities that are loosely related to this experimental protocol ๐Ÿงต. First, there's an effort to define a protocol-independent registry for feature tags, and the plan is for this experimental protocol to be one of its clients. Second, there's research going on into content negotiation systems for other transport protocols (like internet mail and internet fax) and into generalized negotiation systems for multiple transport protocols. At the time of writing, it's unclear if or when this research will produce complete negotiation system specifications. It's also unclear how much future specifications can or will reuse elements of this experimental protocol.

1.1. Background ๐Ÿ“š

Content negotiation has been considered important for the web infrastructure since the early days of the web. The expected benefits of a sufficiently powerful content negotiation system are:

  • smooth deployment of new data formats and markup tags will allow graceful evolution of the web
  • no more choosing between a state of the art multimedia homepage and one that everybody can view
  • good service to a wider range of browsing platforms (from low-end PDA's to high-end VR setups)
  • no more error-prone and cache-unfriendly User-Agent based negotiation
  • sites can be built without click here for the X version links
  • internationalization, plus the ability to offer multi-lingual content without a bias towards one language

2. Terminology ๐ŸŽ€

The words "MUST", "MUST NOT", "SHOULD", "SHOULD NOT", and "MAY" in this document are to be interpreted as described in RFC 2119 [4]. Those keywords are doing a lot of work here, so pay attention ๐Ÿ‘‘.

This specification uses the term header as an abbreviation for header field in a request or response message.

2.1. Terms from HTTP/1.1 ๐Ÿงฉ

This specification mostly uses the terminology of the HTTP/1.1 specification [1]. For your convenience, this section reproduces some key terminology definitions from [1] ๐Ÿซถ.

  • request: An HTTP request message.
  • response: An HTTP response message.
  • resource: A network data object or service that can be identified by a URI. Resources may be available in multiple representations (e.g. multiple languages, data formats, size, resolutions) or vary in other ways.
  • content negotiation: The mechanism for selecting the appropriate representation when servicing a request.
  • client: A program that establishes connections for the purpose of sending requests.
  • user agent: The client which initiates a request. These are often browsers, editors, spiders (web-traversing robots), or other end user tools.
  • server: An application program that accepts connections in order to service requests by sending back responses. Any given program may be capable of being both a client and a server; our use of these terms refers only to the role being performed by the program for a particular connection, rather than to the program's capabilities in general. Likewise, any server may act as an origin server, proxy, gateway, or tunnel, switching behavior based on the nature of each request.
  • origin server: The server on which a given resource resides or is to be created.
  • proxy: An intermediary program which acts as both a server and a client for the purpose of making requests on behalf of other clients. Requests are serviced internally or by passing them on, with possible translation, to other servers. A proxy must implement both the client and server requirements of this specification.
  • age: The age of a response is the time since it was sent by, or successfully validated with, the origin server.
  • fresh: A response is fresh if its age has not yet exceeded its freshness lifetime.

2.2. New terms โœจ

transparently negotiable resource ๐Ÿ’– Okay bestie, this is a resource that's identified by one single URI but has multiple representations (variants) attached to it. When someone requests that URI, the server can pick the best representation using the transparent content negotiation mechanism. A transparently negotiable resource always has a variant list bound to it, which can be represented as an Alternates header (defined in section 8.3). Main character energy ๐Ÿ‘‘

variant list ๐Ÿ“œ A list containing variant descriptions, which can be bound to a transparently negotiable resource.

variant description ๐Ÿงพ A machine-readable description of a variant resource, usually found in a variant list. It contains the variant resource URI and various attributes that describe properties of the variant. Variant descriptions are defined in section 5. ๐Ÿฆ‹

variant resource ๐ŸŽ€ A resource you can grab a variant from with a normal HTTP/1.x GET request, meaning a GET request that doesn't use transparent content negotiation. Simple and iconic ๐Ÿ’…

neighboring variant ๐Ÿ˜๏ธ A variant resource is called a neighboring variant resource of some transparently negotiable HTTP resource if the variant resource has a HTTP URL, and if the absolute URL of the variant resource up to its last slash equals the absolute URL of the negotiable resource up to its last slash. Equality is determined with the URI comparison rules in section 3.2.3 of [1].

Being a neighboring variant matters a lot because of security considerations (section 14.2), so please don't skip that part ๐Ÿšจ Not all variants of a negotiable resource need to be neighboring variants. However, access to neighboring variants can be more highly optimized by using remote variant selection algorithms (section 7) and choice responses (section 10.2). โญ

remote variant selection algorithm ๐Ÿค– A standardized algorithm that lets a server sometimes choose a best variant on behalf of a negotiating user agent. The algorithm typically works out whether the Accept- headers in the request contain enough information to make a choice, and if so, which variant is the best one. Using a remote algorithm can speed up the negotiation process, which is very much the vibe ๐Ÿš€

list response ๐Ÿ“‹ A list response returns the variant list of the negotiable resource, but no variant data. A server can generate one when it doesn't want to, or isn't allowed to, return a particular best variant for the request. List responses are defined in section 10.1.

choice response ๐Ÿ‘‘ A choice response returns a representation of the best variant for the request, and may also return the variant list of the negotiable resource. It can be generated when the server has enough information to choose the best variant on behalf of the user agent, but it may only be generated if that best variant is a neighboring variant. Choice responses are defined in section 10.2. Iconic behaviour ๐Ÿ’œ

adhoc response ๐Ÿงจ An adhoc response can be sent by an origin server as an extreme measure, to achieve compatibility with a non-negotiating or buggy client when that compatibility can't be achieved by sending a list or choice response. There are very little requirements on the contents of an adhoc response. Adhoc responses are defined in section 10.3. It's giving last resort ๐Ÿ™ˆ

Accept- headers ๐Ÿ’Œ The request headers: Accept, Accept-Charset, Accept-Language, and Accept-Features.

supports transparent content negotiation ๐Ÿซถ From the viewpoint of an origin server or proxy, a user agent supports transparent content negotiation if and only if it sends a Negotiate header (section 8.4) which indicates such support. No cap, that's the whole test โœจ

server-side override ๐Ÿ•ต๏ธโ€โ™€๏ธ If a request on a transparently negotiated resource is made by a client which supports transparent content negotiation, an origin server is said to perform a server-side override if the server ignores the directives in the Negotiate request header, and instead uses a custom algorithm to choose an appropriate response. A server-side override can sometimes be used to work around known client bugs. It could also be used by protocol extensions on top of transparent content negotiation. Ngl this one is kind of sneaky ๐Ÿ˜ญ


3. Notation ๐Ÿงฎ

The version of BNF used in this document is taken from [1], and many of the nonterminals used are defined in [1]. Note that the underlying charset is US-ASCII.

One new BNF construct is added:

1%rule

It stands for one or more instances of "rule", separated by whitespace:

1%rule =  rule *( 1*LWS rule )

This specification also introduces:

number = 1*DIGIT

short-float = 1*3DIGIT [ "." 0*3DIGIT ]

This specification uses the same conventions as in [1] (see section 1.2 of [1]) for defining the significance of each particular requirement. Same rules, same vibes ๐Ÿ’œ


4. Overview ๐ŸŒธ

This section gives an overview of transparent content negotiation. It starts with a more general discussion of negotiation as provided by HTTP.

4.1. Content negotiation ๐ŸŒˆ

HTTP/1.1 allows web site authors to put multiple versions of the same information under a single resource URI. Each of these versions is called a โ€œvariantโ€. For example, a resource http://x.org/paper could bind to three different variants of a paper:

  1. HTML, English
  2. HTML, French
  3. Postscript, English

Content negotiation is the process by which the best variant is selected when the resource is accessed. The selection is done by matching the properties of the available variants to the capabilities of the user agent and the preferences of the user. It's a match-making situation ๐Ÿ’•

It has always been possible under HTTP to have multiple representations available for one resource, and to return the most appropriate representation for each subsequent request. However, HTTP/1.1 is the first version of HTTP which has provisions for doing this in a cache-friendly way. These provisions include the Vary response header, entity tags, and the If-None-Match request header. HTTP/1.1 really leveled up here ๐Ÿ™Œ

4.2. HTTP/1.0 style negotiation scheme ๐Ÿ“ฎ

The HTTP/1.0 protocol elements allow for a negotiation scheme as follows:

      Server _____ proxy _____ proxy _____ user
      x.org        cache       cache       agent

        < ----------------------------------
        |      GET http://x.org/paper
        |          Accept- headers
      choose
        |
         ---------------------------------- >
                    Best variant

When the resource is accessed, the user agent sends (along with its request) various Accept- headers which express the user agent capabilities and the user preferences. Then the origin server uses these Accept- headers to choose the best variant, which is returned in the response. Okay, so the server does all the work here ๐Ÿง 

The biggest problem with this scheme is that it does not scale well. For all but the most minimal user agents, Accept- headers expressing all capabilities and preferences would be very large, and sending them in every request would be hugely inefficient. This is especially true because only a small fraction of the resources on the web have multiple variants. Bestie, that's so much data for almost nothing ๐Ÿ˜ญ

4.3. Transparent content negotiation scheme ๐Ÿฆ‹

The transparent content negotiation scheme eliminates the need to send huge Accept- headers, and still allows for a selection process that always yields either the best variant, or an error message indicating that the user agent is not capable of displaying any of the available variants. Chef's kiss ๐Ÿ‘ฉโ€๐Ÿณ

Under the transparent content negotiation scheme, the server sends a list with the available variants and their properties to the user agent. An example of a list with three variants is:

      {"paper.1" 0.9 {type text/html} {language en}},
      {"paper.2" 0.7 {type text/html} {language fr}},
      {"paper.3" 1.0 {type application/postscript} {language en}}

The syntax and semantics of the variant descriptions in this list are covered in section 5. When the list is received, the user agent can choose the best variant and retrieve it. Graphically, the communication looks like this:

      Server _____ proxy _____ proxy _____ user
      x.org        cache       cache       agent

        < ----------------------------------
        |      GET http://x.org/paper
        |
        ----------------------------------- >         [list response]
                  return of list            |
                                         choose
                                            |
        < ----------------------------------
        |  GET http://x.org/paper.1
        |
         ---------------------------------- >         [normal response]
                return of paper.1

The first response returning the list of variants is called a โ€œlist responseโ€. The second response is a normal HTTP response: it does not contain special content negotiation related information. Only the user agent needs to know that the second request actually retrieves a variant. For the other parties in the communication, the second transaction is indistinguishable from a normal HTTP transaction. Undercover queen ๐Ÿ•ถ๏ธ

With this scheme, information about capabilities and preferences is only used by the user agent itself. Therefore, sending such information in large Accept- headers is unnecessary. Accept- headers do have a limited use in transparent content negotiation, however: sending small Accept- headers can often speed up the negotiation process. This is covered in section 4.4. โšก

List responses are covered in section 10.1. As an example, the list response in the above picture could be:

HTTP/1.1 300 Multiple Choices
Date: Tue, 11 Jun 1996 20:02:21 GMT
TCN: list
Alternates: {"paper.1" 0.9 {type text/html} {language en}},
            {"paper.2" 0.7 {type text/html} {language fr}},
            {"paper.3" 1.0 {type application/postscript}
                {language en}}
Vary: negotiate, accept, accept-language
ETag: "blah;1234"
Cache-control: max-age=86400
Content-Type: text/html
Content-Length: 227

<h2>Multiple Choices:</h2>
<ul>
<li><a href=paper.1>HTML, English version</a>
<li><a href=paper.2>HTML, French version</a>
<li><a href=paper.3>Postscript, English version</a>
</ul>

The Alternates header in the response contains the variant list. The Vary header is included to ensure correct caching by plain HTTP/1.1 caches (see section 10.6). The ETag header allows the response to be revalidated by caches, and the Cache-control header controls this revalidation. The HTML entity included in the response lets the user select the best variant by hand if they want to ๐Ÿซถ It's giving backup plan and a glow-up at the same time โœจ

4.4 Optimizing the negotiation process โœจ

Okay so the basic transparent negotiation scheme is two HTTP transactions: one to grab the list, and a second one to grab the chosen variant. BUT there are a few ways to cut corners in the data flow path of the basic scheme, and bestie, they're so clever ๐Ÿ’…

First, caching proxies can cache both variant lists and variants. This kind of caching can reduce the communication overhead, like this ๐Ÿฆ‹

   Server _____ proxy _____ proxy __________ user
   x.org        cache       cache            agent

                              < --------------
                              |  GET ../paper
                              |
                            has the list
                            in cache
                              |
                               -------------  >  [list response]
                                        list  |
                                              |
                                           choose
                                              |
                  < --------------------------
                  |   GET ../paper.1
                  |
               has the variant
               in cache
                  |
                   -------------------------- >  [normal response]
                      return of paper.1

Second, the user agent can send small Accept- headers, which may contain enough info to let the server choose the best variant and return it directly. Iconic ๐ŸŒธ

   Server _____ proxy _____ proxy _____ user
   x.org        cache       cache       agent

     < ----------------------------------
     |      GET http://x.org/paper
     |       small Accept- headers
     |
   able to choose on
   behalf of user agent
     |
      ---------------------------------- >    [choice response]
           return of paper.1 and list

So this choosing based on small Accept- headers is done with a remote variant selection algorithm. That algorithm takes the variant list and the Accept- headers as input. Then it works out whether the Accept- headers have enough info to choose on behalf of the user agent, and if they do, which variant is the best one. If the best variant is a neighboring variant, it may be returned, together with the variant list, in a choice response ๐ŸŽ€

A server may only choose on behalf of a user agent supporting transparent content negotiation if the user agent explicitly allows the use of a particular remote variant selection algorithm in the Negotiate request header. Not me going "that's a whole permission slip" ๐Ÿ’€ User agents with sophisticated internal variant selection algorithms may want to disallow a remote choice, or may want to allow it only when retrieving inline images. And if the local algorithm of the user agent is superior in only some difficult areas of negotiation, it is possible to enable the remote algorithm for the easy areas only. Main character energy for real ๐Ÿ‘‘ More information about using a remote variant selection algorithm can be found in [3].

Choice responses are covered in section 10.2. For example, the choice response in the above picture could be:

HTTP/1.1 200 OK
Date: Tue, 11 Jun 1996 20:05:31 GMT
TCN: choice
Content-Type: text/html
Last-Modified: Mon, 10 Jun 1996 10:01:14 GMT
Content-Length: 5327
Cache-control: max-age=604800
Content-Location: paper.1
Alternates: {"paper.1" 0.9 {type text/html} {language en}},

            {"paper.2" 0.7 {type text/html} {language fr}},
            {"paper.3" 1.0 {type application/postscript}
                {language en}}
Etag: "gonkyyyy;1234"
Vary: negotiate, accept, accept-language
Expires: Thu, 01 Jan 1980 00:00:00 GMT

<title>A paper about ....

Finally, the above two kinds of optimization can be combined. A caching proxy which has the list will sometimes be able to choose on behalf of the user agent, and that can lead to this communication pattern ๐Ÿงš

   Server _____ proxy _____ proxy __________ user
   x.org        cache       cache            agent

                              < ---------------
                              |  GET ../paper
                              |  small Accept
                              |
                           able to choose
                             on behalf
                              |
                  < ----------
                  |  GET ../paper.1
                  |
                   ---------- >   [normal response]
                     paper.1  |
                               ---------------- >  [choice response]
                                paper.1 and list

Note that this cutting of corners not only saves bandwidth, it also eliminates delays due to packet round trip times, and reduces the load on the origin server. Literally everyone wins ๐Ÿซถ

4.5 Downwards compatibility with non-negotiating user agents

To handle requests from user agents which don't support transparent content negotiation, this specification allows the origin server to revert to a HTTP/1.0 style negotiation scheme. The specification of heuristics for such schemes is beyond the scope of this document ๐Ÿ™ˆ

4.6 Retrieving a variant by hand

It is always possible for a user agent to retrieve the variant list which is bound to a negotiable resource. The user agent can use this list to make a menu of all the variants and their characteristics available to the user. That menu lets the user randomly browse other variants, and makes it possible to manually correct any sub-optimal choice made by the automatic negotiation process. Giving the user the final say, iykyk ๐Ÿ’œ

4.7 Dimensions of negotiation

Transparent content negotiation defines four dimensions of negotiation:

  1. Media type (MIME type)
  2. Charset
  3. Language
  4. Features

The first three dimensions have traditionally been present in HTTP. The fourth dimension is added by this specification. Additional dimensions, beyond the four mentioned above, could be added by future specifications โœจ

Negotiation on the content encoding of a response (gzipped, compressed, etc.) is left outside of the realm of transparent negotiation. See section 10.8 for more information ๐ŸŒˆ

4.8 Feature negotiation

Feature negotiation is meant to cover all the areas of negotiation that aren't covered by the type, charset, and language dimensions. Examples are negotiation on:

  • HTML extensions
  • Extensions of other media types
  • Color capabilities of the user agent
  • Screen size
  • Output medium (screen, paper, ...)
  • Preference for speed vs. preference for graphical detail

The feature negotiation framework (section 6) is the principal means by which transparent negotiation offers extensibility. A new dimension of negotiation (really a sub-dimension of the feature dimension) can be added without a new standards effort, just by the simple registration of a feature tag. Zero drama, all vibes ๐Ÿฆ‹

4.9 Length of variant lists

As a general rule, variant lists should be short. It's expected that a typical transparently negotiable resource will have 2 to 10 variants, depending on its purpose. Variant lists should be short for a few reasons:

  1. The user must be able to pick a variant by hand to correct a bad automatic choice, and this is harder with a long variant list.
  2. A large number of variants will decrease the efficiency of internet proxy caches.
  3. Long variant lists will make some transparently negotiated responses longer.

In general, it's not desirable to create a transparently negotiable resource with hundreds of variants just to fine-tune the graphical presentation of a resource ๐Ÿ˜ญ Any graphical fine-tuning should be done, as much as possible, by using constructs which act on the user agent side, for example:

<center><img src=titlebanner.gif width=100%
alt="MegaBozo Corp"></center>

To promote user agent side fine-tuning, which is more scalable than fine-tuning over the network, user agents which implement a scripting language for content rendering are encouraged to make that language's availability visible for transparent content negotiation. They should also let rendering scripts access the capabilities and preferences data used for content negotiation, as far as privacy considerations permit this ๐Ÿ”ฅ

4.10 Relation with other negotiation schemes

The HTTP/1.x protocol suite allows for many different negotiation mechanisms. Transparent content negotiation specializes in scalable, interoperable negotiation of content representations at the HTTP level. Transparent negotiation is intended to co-exist with other negotiation schemes, both open and proprietary, which cover different application domains or work at different points in the author-to-user chain. Ultimately, it's up to the resource author to decide which negotiation mechanism, or combination of mechanisms, is most appropriate for the task at hand ๐Ÿ’…


5 Variant descriptions ๐ŸŽ€

5.1 Syntax

A variant can be described in a machine-readable way with a variant description.

    variant-description =
               "{" <"> URI <"> source-quality *variant-attribute"}"

    source-quality = qvalue

    variant-attribute = "{" "type" media-type "}"
                      | "{" "charset" charset "}"
                      | "{" "language"  1#language-tag "}"
                      | "{" "length" 1*DIGIT "}"
                      | "{" "features" feature-list "}"
                      | "{" "description"
                                  quoted-string [ language-tag ] "}"
                      | extension-attribute

    extension-attribute = "{" extension-name extension-value "}"
    extension-name      = token
    extension-value     = *( token | quoted-string | LWS
                           | extension-specials )

    extension-specials  =
                       <any element of tspecials except <"> and "}">

The feature-list syntax is defined in section 6.4.

Examples are:

   {"paper.2" 0.7 {type text/html} {language fr}}

   {"paper.5" 0.9 {type text/html} {features tables}}

   {"paper.1" 0.001}

The various attributes which can be present in a variant description are covered in the subsections below. Each attribute may appear only once in a variant description ๐Ÿงš

5.2 URI

The URI attribute gives the URI of the resource from which the variant can be retrieved with a GET request. It can be absolute or relative to the Request-URI. The variant resource may vary (on the Cookie request header, for example), but it MUST NOT engage in transparent content negotiation itself ๐Ÿ™ˆ

5.3. Source-quality ๐Ÿ’–

Okay so source-quality tells you how good a variant is as a representation of the negotiable resource, assuming it gets rendered by a perfect rendering engine on the best possible output medium. It's giving quality check โœจ

If the source-quality is less than 1, it's usually a sign that a lossy conversion to some data format made things worse ๐Ÿ“‰ For example, a picture that started as JPEG would have a lower source quality once it's turned into XBM, and an even lower one once it's turned into ASCII-art. ๐Ÿ’€ But here's the twist: the degradation depends on the source. An original piece of ASCII-art could lose quality if you captured it as JPEG ๐Ÿ™ˆ

Source-quality can also reflect how skilled a language translation is, or how well the media type can capture the artistic vision behind the original ๐ŸŽจ

Servers should use this table as a guide when assigning source-quality values ๐Ÿ“

      1.000  perfect representation
      0.900  threshold of noticeable loss of quality
      0.800  noticeable, but acceptable quality reduction
      0.500  barely acceptable quality
      0.300  severely degraded quality
      0.000  completely degraded quality

Local variant selection algorithms (see appendix 19) can use the same table when they assign degradation factors for different content rendering mechanisms ๐Ÿงš Notice that almost all the meaningful values here are close to 1. That's because quality factors get combined by multiplying them, not adding them, so small numbers stack up fast ๐Ÿ˜ญ

When assigning source-quality values, servers should not account for the size of the variant or how it affects transmission and rendering delays. The size belongs in the length attribute, and any size-dependent math should happen in the variant selection algorithm ๐Ÿ“ฆ Any constant rendering delay for a particular media type (like the startup time of a helper application) should be accounted for by the user agent when it assigns a quality factor to that media type ๐ŸŒธ

5.4. Type, charset, language, and length ๐ŸŽ€

The type attribute of a variant description carries the same info as its Content-Type response header counterpart defined in [1], except for any charset information. That charset info MUST be carried in the charset attribute ๐Ÿ’… For example, this header

Content-Type: text/html; charset=ISO-8859-4

has these counterpart attributes

{type text/html} {charset ISO-8859-4}

The language and length attributes carry the same information as their Content-* response header counterparts in [1]. So the length attribute, if it's there, MUST reflect the length of the variant alone ๐Ÿ“ It must NOT include the total size of the variant plus any objects it inlines or embeds. Keep it clean, bestie ๐Ÿซถ

Every one of these attributes is optional, but it's often desirable to include as many as you can, because that boosts the quality of the negotiation process ๐Ÿ”ฅ

Note: A server is not required to keep a one-to-one match between the attributes in the variant description and the Content-* headers in the variant response. For example, if the variant description has a language attribute, the response doesn't necessarily need a Content-Language header. And if a Content-Language header is present, it doesn't have to be an exact copy of what the language attribute says ๐Ÿฆ‹

5.5. Features ๐ŸŒˆ

The features attribute says how the presence or absence of particular feature tags in the user agent affects the overall quality of the variant. This attribute gets its own full explanation in section 6.4 โœจ

5.6. Description ๐Ÿ“

The description attribute gives a textual description of the variant. You'd add it when the URI and the normal attributes are too opaque for the user to make sense of ๐Ÿง If a user agent is showing a menu of available variants built from a variant list, and a variant has a description attribute, the user agent SHOULD show that description instead of the variant's normal attributes ๐Ÿ‘‘

The description field uses the UTF-8 character encoding scheme [5], which is a superset of US-ASCII, with "%" HEX HEX encoding. The optional language tag MAY be used to specify the language of the description text ๐ŸŒ

5.7. Extension-attribute ๐Ÿงช

The extension-attribute lets future specs slowly add dimensions of negotiation that can't be made with the feature negotiation framework, and it makes content negotiation experiments way easier ๐Ÿ”ฌ In experimental situations, servers MUST ONLY generate extension-attributes whose names start with x- ๐Ÿšซ User agents SHOULD ignore any extension attributes they don't recognize ๐Ÿ™ˆ And proxies MUST NOT run a remote variant selection algorithm if an unknown extension attribute is in the variant list. No cap, that one's serious ๐Ÿ˜ค


6. Feature negotiation ๐Ÿฆ‹

This section defines the feature negotiation mechanism. We already met feature negotiation back in section 4.8, and appendix 19 has examples of it if you want to see it in action ๐Ÿงš

6.1. Feature tags ๐Ÿท๏ธ

A feature tag (ftag) identifies something you can negotiate on. That could be a property of a representation, a capability of a user agent, or a user's preference for a certain type of representation โœจ Feature tags don't have to be limited to transparent content negotiation, and not every feature tag has to be usable in the HTTP transparent content negotiation framework ๐Ÿ’œ

ftag = token | quoted-string
Note: A protocol-independent system for registering feature tags is currently being developed in the IETF. This specification doesn't define any feature tags yet. In experimental situations, tags that start with x. are encouraged ๐ŸŒธ

Feature tags show up in feature sets (section 6.2) and in feature predicates (section 6.3). Feature predicates are used in features attributes (section 6.4), and those attributes live in variant descriptions (section 5). Variant descriptions can be sent in Alternates headers (section 8.3) ๐Ÿ’…

The US-ASCII charset is used for feature tags, and feature tag comparison is case-insensitive ๐Ÿ”ค A token tag XYZ is equal to a quoted-string tag "XYZ". Examples include

tables, fonts, blebber, wolx, screenwidth, colordepth

Here's how feature tags look inside a variant description ๐ŸŽ€

{"index.html" 1.0 {type text/html} {features tables frames}}

This spec follows general computing practice and places no restrictions on what may be called a feature ๐ŸŒˆ At the protocol level, it doesn't tell apart the different uses of feature tags. A tag gets processed the same way whether it names a property, a capability, or a preference ๐Ÿง  For some tags it's kind of fluid which one it is. For example, in content negotiation on web pages, a "textonly" tag identifies the capability of a text-only user agent, but a person using a graphical user agent might use that same tag to say they prefer text-only content over graphical content ๐Ÿ™ˆ

6.1.1. Feature tag values ๐Ÿฌ

A feature tag's definition may say that it can have zero, one, or more values attached. Those values make the meaning of the tag more specific โœจ For example, a feature tag paper' could come with the values A4' and A5'.

tag-value  = token | quoted-string

The US-ASCII charset is used for feature tag values. Equality comparison for tag values MUST be done with a case-sensitive, octet-by-octet comparison, and any "%" HEX HEX encodings MUST be processed as in [1] ๐Ÿ” A token value XYZ is equal to a quoted-string value "XYZ".

6.2. Feature sets ๐Ÿงบ

The feature set of a user agent is a data structure that records the user agent's capabilities and the user's preferences ๐Ÿ’–

Local variant selection algorithms use feature sets (see appendix 19 for an example). A user agent can use the Accept-Features header (section 8.2) to share some of what's in its feature set with remote variant selection algorithms ๐Ÿ“ฌ

Structurally, a feature set is a possibly empty set full of records that look like this:

( feature tag , set of feature tag values )

If a record with a feature tag is in the set, that means the user agent implements the matching capability, or the user has expressed the matching preference โœ…

Each record in a feature set has a set of tag values, which may be empty ๐Ÿซง For feature tags that can't have values, this set is always empty. For feature tags that can have zero, one, or more values, the set holds whatever values are currently attached to the tag. If the set for a feature tag T contains the value V, we say "the tag T is present with the value V" ๐ŸŽฏ

This spec doesn't define a standard notation for feature sets. Here's a tiny one in mathematical notation ๐Ÿงฎ

{ ( "frames" , { } ) ,
  ( "paper"  , { "A4" , "A5" } )
}

Feature registration is an ongoing process, so a user agent usually can't know the meaning of every feature tag it might run into in a variant description ๐Ÿ˜ตโ€๐Ÿ’ซ A user agent SHOULD treat all feature tags it doesn't know as absent from its feature set ๐Ÿ‘ป

A user agent may change what's in its feature set depending on the type of request, and it may update the set as conditions change, like when the window size changes ๐ŸชŸ That's why, when you're thinking about feature negotiation, you usually talk about "the feature set of the current request" ๐Ÿ’œ

6.3. Feature predicates ๐Ÿ’…

Okay, so feature predicates are basically yes-or-no questions you can ask about a feature set. ๐Ÿงš They show up in the features attribute of a variant description, bestie.

fpred = [ "!" ] ftag
      | ftag ( "=" | "!=" ) tag-value
      | ftag "=" "[" numeric-range "]"

numeric-range = [ number ] "-" [ number ]

Feature predicates get used in features attributes (section 6.4), and those live inside variant descriptions (section 5). Variant descriptions can be sent along in Alternates headers (section 8.3). It's all connected, iykyk ๐Ÿ”—

Here are some examples that look super cute ๐ŸŽ€

blebber, !blebber, paper=a4, colordepth=5, blex!=54,
dpi=[300-599], colordepth=[24-]

Now, the vibe check. A user agent SHOULD work out whether each predicate is true, using the feature set of the current request. Here's how each one shakes out ๐Ÿ‘‘

ftag       true if the feature is present, false otherwise

!ftag      true if the feature is absent, false otherwise

ftag=V     true if the feature is present with the value V,
           false otherwise,

ftag!=V    true if the feature is not present with the value V,
           false otherwise,

ftag=[N-M] true if the feature is present with at least one
           numeric value, while the highest value with which it
           is present in the range N-M, false otherwise.  If N
           is missing, the lower bound is 0.  If M is missing,
           the upper bound is infinity.

Let's do a little example to see it in action โœจ Say we have this feature set:

{ ( "blex"       , { } ),
  ( "colordepth" , { "5" } ),
  ( "UA-media"   , { "stationary" } ),
  ( "paper"      , { "A4", "A3" } ) ,
  ( "x-version"  , { "104", "200" } )
}

With that set, these predicates are true ๐ŸŒˆ

blex, colordepth=[4-], colordepth!=6, colordepth, !screenwidth, UA-
media=stationary, UA-media!=screen, paper=A4, paper =!A0,
colordepth=[ 4 - 6 ], x-version=[100-300], x-version=[200-300]

And these ones are false ๐Ÿ’€

!blex, blebber, colordepth=6, colordepth=foo, !colordepth,
screenwidth, screenwidth=640, screenwidth!=640, x-version=99, UA-
media=screen, paper=A0, paper=a4, x-version=[100-199], wuxta

6.4. Features attribute ๐ŸŒธ

The features attribute has its syntax defined in section 5.1, and it's written like this:

"{" "features" feature-list "}"

Its whole job is to say how whether a feature tag is present or absent in the user agent changes the overall quality of the variant. It's giving main character energy, literally ๐Ÿฆ‹

feature-list = 1%feature-list-element

feature-list-element = ( fpred | fpred-bag )
                       [ ";" [ "+" true-improvement  ]
                             [ "-" false-degradation ]
                       ]

fpred-bag = "[" 1%fpred "]"

true-improvement   =  short-float
false-degradation  =  short-float

Features attributes live inside variant descriptions (section 5), and those can be sent in Alternates headers (section 8.3). Here are a couple of examples that are so cute ๐Ÿง

{features !textonly [blebber !wolx] colordepth=3;+0.7}

{features !blink;-0.5 background;+1.5 [blebber !wolx];+1.4-0.8}

So the defaults work like this ๐Ÿ“ The default true-improvement is 1. The default false-degradation is 0, unless you've given a true-improvement value, in which case it's 1.

Now for the math part, which is lowkey important. A user agent SHOULD compute the quality degradation factor for the features attribute, and a remote variant selection algorithm MUST do it too. You do this by multiplying together all the quality degradation factors of the elements in the feature-list. Heads up, the result can be a factor bigger than 1 โœจ

Last thing: a feature list element gives you its true-improvement factor if its feature predicate is true, or if at least one element in its matching fpred-bag is true. Otherwise, it gives you its false-degradation factor. Periodt ๐Ÿ’…


7. Remote variant selection algorithms ๐Ÿฆ‹

Okay, so a remote variant selection algorithm is a standardized algorithm that lets a server pick the best variant on behalf of a user agent that's negotiating. The best part is that it can speed up negotiation by getting rid of a request-response round trip, which is so efficient, I'm obsessed ๐Ÿ˜ญ

A remote algorithm usually figures out whether the Accept- headers in the request have enough info to make a choice. If they do, it also works out which variant is the best one. This specification doesn't define any remote algorithms itself, but it does define a way to negotiate whether those algorithms get used. Bestie, that's the whole plot ๐ŸŽฌ

7.1. Version numbers ๐Ÿ”ข

Each remote variant selection algorithm gets a version number, so you can tell different algorithms apart ๐Ÿ’œ

rvsa-version = major "." minor

major = 1*4DIGIT
minor = 1*4DIGIT

Here's the rule that matters most ๐Ÿซถ An algorithm with version number X.Y, where Y>0, MUST be downwards compatible with all the algorithms from X.0 up to X.Y. Downwards compatibility means that if the newer algorithm gets the same information, it MUST make the same choice as the old one, or a better choice. And there are no compatibility requirements between algorithms with different major version numbers, so don't stress about those ๐ŸŒˆ


8. Content negotiation status codes and headers ๐ŸŽ€

Alright, so this specification adds one new HTTP status code, and it introduces six new HTTP headers. It also extends what an existing HTTP/1.1 header means. Lots going on, but it's all good โœจ

8.1. 506 Variant Also Negotiates ๐Ÿ™ˆ

The 506 status code tells you the server has an internal configuration error. The chosen variant resource is set up to do transparent content negotiation itself, which means it isn't a proper end point in the negotiation process. Not me, sending a variant to negotiate with itself ๐Ÿ’€

8.2. Accept-Features ๐Ÿงš

A user agent can use the Accept-Features request header to share info about whether certain features are present or absent in the feature set of the current request. Servers can use that info when they're running a remote variant selection algorithm ๐Ÿฆ‹

Note: the name Accept-Features' for this header was chosen because of symmetry considerations with other Accept- headers, even though the Accept-Features header will generally not contain an exhaustive list of features which are somehow accepted'. A more accurate name of this header would have been `Feature-Set-Info'.

Here's the grammar, babe ๐Ÿ“

Accept-Features = "Accept-Features" ":"
            #( feature-expr *( ";" feature-extension ) )

feature-expr = [ "!" ] ftag
             | ftag ( "=" | "!=" ) tag-value
             | ftag "=" "{" tag-value "}"
             | "*"

feature-extension = token [ "=" ( token | quoted-string ) ]

No feature extensions are defined in this specification, so there's nothing extra to worry about yet. Here's an example โœจ

Accept-Features: blex, !blebber, colordepth={5}, !screenwidth,
           paper = A4, paper!="A2", x-version=104, *

Each feature expression has its own meaning, and here they are ๐Ÿ’–

ftag       ftag is present

!ftag      ftag is absent

ftag=V     ftag is present with the value V

ftag!=V    ftag is present, but not with the value V

ftag={V}   ftag is present with the value V, and not with any
           other values

*          the expressions in this header do not fully describe
           the feature set: feature tags not mentioned in this
           header may also be present, and, except for the case
           ftag={V}, tags may be present with more values than
           mentioned.

If there's no Accept-Features header in a request, that's the same as including this:

Accept-Features: *

Now here's the cool part ๐Ÿ”ฅ By using the Accept-Features header, a remote variant selection algorithm can sometimes figure out whether a feature predicate is true on behalf of the user agent. For example, with this header:

Accept-Features: blex, !blebber, colordepth={5}, !screenwidth,
           paper = A4, paper!="A2", x-version=104, *

the algorithm can tell that these predicates are true:

blex, colordepth=[4-], colordepth!=6, colordepth, !screenwidth,
paper=A4, colordepth=[4-6]

It can also tell that these are false:

!blex, blebber, colordepth=6, colordepth=foo, !colordepth,
screenwidth, screenwidth=640, screenwidth!=640,

But for these, it can't determine the truth value at all ๐Ÿ™ˆ

UA-media=stationary, UA-media!=screen, paper!=a0,
x-version=[100-300], x-version=[200-300], x-version=99,
UA-media=screen, paper=A0, paper=a4, x-version=[100-199], wuxta

8.3. Alternates ๐Ÿ’œ

The Alternates response header is how you share the list of variants bound to a negotiable resource. This list can also include directives for any content negotiation process, so it's basically a whole group chat ๐Ÿ’ฌ If a response from a transparently negotiable resource includes an Alternates header, this header MUST contain the complete variant list bound to the negotiable resource. Responses from resources that don't support transparent content negotiation MAY also use Alternates headers, no pressure ๐Ÿซถ

Alternates = "Alternates" ":" variant-list

variant-list = 1#( variant-description
                 | fallback-variant
                 | list-directive )

fallback-variant = "{" <"> URI <"> "}"

list-directive = ( "proxy-rvsa" "=" <"> 0#rvsa-version <"> )

                 | extension-list-directive

extension-list-directive =
                 token [ "=" ( token | quoted-string ) ]

Here's an example that's giving everything ๐ŸŒˆ

Alternates: {"paper.1" 0.9 {type text/html} {language en}},
            {"paper.2" 0.7 {type text/html} {language fr}},
            {"paper.3" 1.0 {type application/postscript}
                {language en}},
            proxy-rvsa="1.0, 2.5"

Any relative URI in a variant-description or fallback-variant field is relative to the request-URI. Only one fallback-variant field may be present. If the variant selection algorithm of the user agent finds that all the described variants are unacceptable, it SHOULD choose the fallback variant, if there is one, as the best variant. If the user agent computes the overall quality values of the described variants and finds that several variants share the highest value, then the first variant with that value in the list SHOULD be chosen as the best variant. Tie-breakers with a first-come vibe ๐Ÿง

The proxy-rvsa directive restricts how proxies use remote variant selection algorithms. If it's present, a proxy MUST ONLY use algorithms that have one of the version numbers listed, or that have the same major version number and a higher minor version number than one of the listed versions. Any restrictions set by proxy-rvsa come on top of the restrictions the user agent set in the Negotiate request header. The directive proxy-rvsa="" will disable variant selection by proxies entirely, so that's a big ol' stop sign ๐Ÿ›‘ And clients SHOULD ignore all extension-list-directives they don't understand.

One last thing ๐Ÿ’– A variant list may contain multiple differing descriptions of the same variant. This can be convenient if the variant uses conditional rendering constructs, or if the variant resource returns multiple representations using a multipart media type. Lowkey genius, honestly ๐Ÿ‘‘

8.4 Negotiate โœจ

okay bestie, the Negotiate request header is where the user agent drops its directives for any content negotiation process the request kicks off ๐Ÿ’…

Negotiate = "Negotiate" ":" 1#negotiate-directive

negotiate-directive = "trans"
                    | "vlist"
                    | "guess-small"
                    | rvsa-version
                    | "*"
                    | negotiate-extension

negotiate-extension = token [ "=" token ]

Here are some examples ๐Ÿ“

Negotiate: 1.0, 2.5
Negotiate: *

So these are the negotiate directives and what they mean ๐Ÿฆ‹

  • "trans": The user agent supports transparent content negotiation for the current request.
  • "vlist": The user agent requests that any transparently negotiated response for the current request includes an Alternates header with the variant list bound to the negotiable resource. Implies "trans".
  • "guess-small": The user agent allows origin servers to run a custom algorithm which guesses the best variant for the request, and to return this variant in a choice response, if the resulting choice response is smaller than or not much larger than a list response. The definition of `not much larger' is left to origin server heuristics. Implies "vlist" and "trans".
  • rvsa-version: The user agent allows origin servers and proxies to run the remote variant selection algorithm with the indicated version number, or with the same major version number and a higher minor version number. If the algorithm has sufficient information to choose a best, neighboring variant, the origin server or proxy MAY return a choice response with this variant. Implies "trans".
  • "*": The user agent allows origin servers and proxies to run any remote variant selection algorithm. The origin server may even run algorithms which have not been standardized. If the algorithm has sufficient information to choose a best, neighboring variant, the origin server or proxy MAY return a choice response with this variant. Implies "trans".

Servers SHOULD ignore all negotiate-directives they do not understand. If the Negotiate header allows a choice between multiple remote variant selection algorithms which are all supported by the server, the server SHOULD use some internal precedence heuristics to select the best algorithm. ๐Ÿ”ฅ


8.5 TCN ๐Ÿ’–

the TCN response header is how a server says "hey, this resource is transparently negotiated" ๐ŸŽ€

TCN = "TCN" ":" #( response-type
                 | server-side-override-directive
                 | tcn-extension )

response-type = "list" | "choice" | "adhoc"

server-side-override-directive = "re-choose" | "keep"

tcn-extension = token [ "=" ( token | quoted-string ) ]

okay so if the resource is not transparently negotiated, a TCN header MUST NOT be included in any response. If it is transparently negotiated, a TCN header, which includes the response-type value of the response, MUST be included in every response with a 2xx status code or any 3xx status code, except 304, in which it MAY be included. A TCN header MAY also be included, without a response-type value, in other responses from transparently negotiated resources. ๐ŸŒธ

A server-side override directive MUST be included if the origin server performed a server-side override when choosing the response. If the directive is "re-choose", the server MUST include an Alternates header with the variant bound to the negotiable resource in the response, and user agent SHOULD use its internal variant selection algorithm to choose, retrieve, and display the best variant from this list. If the directive is "keep" the user agent SHOULD NOT renegotiate on the response, but display it directly, or act on it directly if it is a redirection response. ๐Ÿ’…

Clients SHOULD ignore all tcn-extensions they do not understand. ๐Ÿซถ


8.6 Variant-Vary ๐Ÿงš

the Variant-Vary response header can show up in a choice response to record any vary information that applies to the variant data (the entity body combined with some of the entity headers) inside the response, rather than to the response as a whole. so it's a per-variant vibe ๐Ÿฆ‹

Variant-Vary  = "Variant-Vary" ":" ( "*" | 1#field-name )

Use of the Variant-Vary header is discussed in section 10.2. ๐ŸŒˆ


9 Cache validators โœจ

to allow for correct and efficient caching and revalidation of negotiated responses, this specification extends the caching model of HTTP/1.1 [1] in various ways. ๐Ÿ“

This specification does not introduce a `variant-list-max-age' directive which explicitly bounds the freshness lifetime of a cached variant list, like the `max-age' Cache-Control directive bounds the freshness lifetime of a cached response. However, this specification does ensure that a variant list which is sent at a time T by the origin server will never be re-used without revalidation by semantically transparent caches after the time T+M. This M is the maximum of all freshness lifetimes assigned (using max-age directives or Expires headers) by the origin server to

a. the responses from the negotiable resource itself, and

b. the responses from its neighboring variant resources

If no freshness lifetimes are assigned by the origin server, M is the maximum of the freshness lifetimes which were heuristically assigned by all caches which can re-use the variant list. ๐Ÿ”ฅ


9.1 Variant list validators ๐ŸŽ€

a variant list validator is an opaque value which acts as the cache validator of a variant list bound to a negotiable resource. ๐Ÿ’–

variant-list-validator = <quoted-string not containing any ";">

If two responses contain the same variant list validator, a cache can treat the Alternates headers in these responses as equivalent (though the headers themselves need not be identical). ๐Ÿซถ


9.2 Structured entity tags ๐Ÿ’œ

a structured entity tag is a normal entity tag whose opaque string gets extended with a semicolon followed by the text (without the surrounding quotes) of a variant list validator. basically it's the etag and the vlv hanging out together ๐Ÿ‘‘

     normal      |  variant list  |   structured
     entity tag  |  validator     |   entity tag
    -------------+----------------+-----------------
      "etag"     |     "vlv"      |   "etag;vlv"
     W/"etag"    |     "vlv"      |  W/"etag;vlv"

ngl this is so cute. Note that a structured entity tag is itself also an entity tag. The structured nature of the tag lets caching proxies that can do transparent content negotiation perform some optimizations defined in section 10. When not performing such optimizations, a structured tag SHOULD be treated as a single opaque value, according to the general rules in HTTP/1.1. Examples of structured entity tags are:

"xyzzy;1234"  W/"xyzzy;1234"  "gonkxxxx;1234"  "a;b;c;;1234"

in that last example, the normal entity tag is "a;b;c;" and the variant list validator is "1234". the more you know โœจ

If a transparently negotiated response includes an entity tag, it MUST be a structured entity tag. The variant list validator in the structured tag MUST act as a validator for the variant list contained in the Alternates header. The normal entity tag in the structured tag MUST act as a validator of the entity body in the response and of all entity headers except Alternates. ๐Ÿ”ฅ


9.3 Assigning entity tags to variants ๐Ÿฆ‹

to allow for correct revalidation of transparently negotiated responses by clients, origin servers SHOULD generate all normal entity tags for the neighboring variant resources of the negotiable resource in such a way that

  1. the same tag is never used by two different variants, unless this tag labels exactly the same entity on all occasions,
  2. if one normal tag "X" is a prefix of another normal tag "XY", then "Y" must never be a semicolon followed by a variant list validator.

10 Content negotiation responses ๐ŸŒธ

if a request on a transparently negotiated resource yields a response with a 2xx status code or any 3xx status code except 304, this response MUST always be either a list response, a choice response, or an adhoc response. These responses MUST always include a TCN header which specifies their type. Transparently negotiated responses with other status codes MAY also include a TCN header. ๐Ÿ’…

The conditions under which the different content negotiation responses may be sent are defined in section 12.1 for origin servers and in section 13 for proxies. ๐Ÿงš

After having constructed a list, choice, or adhoc response, a server MAY process any If-No-Match or If-Range headers in the request message and shorten the response to a 304 (Not Modified) or 206 (Partial Content) response, following the rules in the HTTP/1.1 specification [1]. In this case, the entity tag of the shortened response will identify it indirectly as a list, choice, or adhoc response. ๐ŸŽ€

10.1. List response

Okay so a list response just hands back the variant list of the negotiable resource ๐Ÿ“‹ but no variant data ๐Ÿ™…โ€โ™€๏ธ. It's basically "here are your options, bestie, you pick" ๐Ÿ’–. A server might send one when it doesn't want to pick a best variant for the request, or when it isn't allowed to. And if the user agent does transparent content negotiation โœจ, the list response tells it to pick the best variant and go grab it itself. Iconic ๐Ÿ’….

A list response MUST contain (besides the normal headers required by HTTP) a TCN header which specifies the "list" response-type, the Alternates header bound to the negotiable resource, a Vary header and (unless it was a HEAD request) an entity body which allows the user to manually select the best variant. ๐ŸŽ€

Here's an example of a list response ๐Ÿ‘‡

HTTP/1.1 300 Multiple Choices
Date: Tue, 11 Jun 1996 20:02:21 GMT
TCN: list
Alternates: {"paper.1" 0.9 {type text/html} {language en}},
            {"paper.2" 0.7 {type text/html} {language fr}},
            {"paper.3" 1.0 {type application/postscript}
                {language en}}
Vary: negotiate, accept, accept-language
ETag: "blah;1234"
Cache-control: max-age=86400
Content-Type: text/html
Content-Length: 227

<h2>Multiple Choices:</h2>
<ul>
<li><a href=paper.1>HTML, English version</a>
<li><a href=paper.2>HTML, French version</a>
<li><a href=paper.3>Postscript, English version</a>
</ul>
Note: A list response can have any status code, but the 300 (Multiple Choices) code is the most appropriate one for HTTP/1.1 clients. Some existing versions of HTTP/1.0 clients are known to silently ignore 300 responses, instead of handling them according to the HTTP/1.0 specification [2]. Servers should therefore be careful in sending 300 responses to non-negotiating HTTP/1.0 user agents, and in making these responses cacheable. The 200 (OK) status code can be used instead. ๐Ÿ™ˆ

The Vary header in the response SHOULD ensure correct handling by plain HTTP/1.1 caching proxies. ๐Ÿฆ‹ This header can either be

Vary: *

or a more elaborate header; see section 10.6.1.

Only the origin server may construct list responses. Depending on the status code, a list response is cacheable unless indicated otherwise. ๐Ÿ’œ

According to the HTTP/1.1 specification [1], a user agent which does not support transparent content negotiation will, when receiving a list response with the 300 status code, display the entity body included in the response. If the response contains a Location header, however, the user agent MAY automatically redirect to this location. ๐ŸŒธ

The handling of list responses by clients supporting transparent content negotiation is described in sections 11.1 and 13. ๐Ÿงš


10.2. Choice response

A choice response returns a representation of the best variant for the request, and may also return the variant list of the negotiable resource. โœจ It can be generated when the server has sufficient information to be able to choose the best variant on behalf of the user agent, but may only be generated if this best variant is a neighboring variant. For requests from user agents which do not support transparent content negotiation, a server may always generate a choice response, provided that the variant returned is a neighboring variant. The variant returned in a choice response need not necessarily be listed in the variant list bound to the negotiable resource. ๐Ÿ“

A choice response merges a normal HTTP response from the chosen variant, a TCN header which specifies the "choice" response-type, and a Content-Location header giving the location of the variant. Depending on the status code, a choice response is cacheable unless indicated otherwise. ๐ŸŒˆ

Origin servers and proxy caches MUST construct choice responses with the following algorithm (or any other algorithm which gives equal end results for the client). ๐Ÿ‘‘

In this algorithm, the current Alternates header' refers to the Alternates header containing the variant list which was used to choose the best variant, and the current variant list validator' refers to the validator of this list. Section 10.4 specifies how these two items can be obtained by a proxy cache. ๐Ÿ”

The algorithm consists of four steps. Let's go ๐Ÿš€

  1. Construct an HTTP request message on the best variant resource by rewriting the request-URI and Host header (if appropriate) of the received request message on the negotiable resource.
  2. Generate a valid HTTP response message, but not one with the 304 (Not Modified) code, for the request message constructed in step 1.

In a proxy cache, the response can be obtained from cache memory, or by passing the constructed HTTP request towards the origin server. If the request is passed on, the proxy MAY add, modify, or delete If-None-Match and If-Range headers to optimize the transaction with the upstream server.

Note: the proxy should be careful not to add entity tags of non-neighboring variants to If-* (conditional) headers of the request, as there are no global uniqueness requirements for these tags. โš ๏ธ

  1. Only in origin servers: check for an origin server configuration error. If the HTTP response message generated in step 2 contains a TCN header, then the best variant resource is not a proper end point in the transparent negotiation process, and a 506 (Variant Also Negotiates) error response message SHOULD be generated instead of going to step 4. ๐Ÿ’€
  2. Add a number of headers to the HTTP response message generated in step 2.
    • a. Add a TCN header which specifies the "choice" response-type.
    • b. Add a Content-Location header giving the location of the chosen variant. Delete any Content-Location header which was already present.

Note: According to the HTTP/1.1 specification [1], if the Content-Location header contains a relative URI, this URI is relative to the URI in the Content-Base header, if present, and relative to the request-URI if no Content-Base header is present.

  • c. If any Vary headers are present in the response message from step 2, add, for every Vary header, a Variant-Vary header with a copy of the contents of this Vary header.
  • d. Delete any Alternates headers which are present in in the response. Now, the current Alternates header MUST be added if this is required by the Negotiate request header, or if the server returns "re-choose" in the TCN response header. Otherwise, the current Alternates header MAY be added.

Note: It is usually a good strategy to always add the current Alternates header, unless it is very large compared to the rest of the response. ๐Ÿซถ

  • e. Add a Vary header to ensure correct handling by plain HTTP/1.1 caching proxies. This header can either be
Vary: *

or a more elaborate header, see section 10.6.

  • f. To ensure compatibility with HTTP/1.0 caching proxies which do not recognize the Vary header, an Expires header with a date in the past MAY be added. See section 10.7 for more information. ๐Ÿ•ฐ๏ธ
  • g. If an ETag header is present in the response message from step 2, then extend the entity tag in that header with the current variant list validator, as specified in section 9.2.

Note: Step g. is required even if the variant list itself is not added in step d. ๐Ÿ’…

  • h. Only in proxy caches: set the Age header of the response to
max( variant_age , alternates_age )

where variant_age is the age of the variant response obtained in step 2, calculated according to the rules in the HTTP/1.1 specification [1], and alternates_age is the age of the Alternates header added in step d, calculated according to the rules in section 10.4. โณ

Note that a server can shorten the response produced by the above algorithm to a 304 (Not Modified) response if an If-None-Match header in the original request allows it. If this is the case, an implementation of the above algorithm can avoid the unnecessary internal construction of the full response message in step 2, and it need only construct the parts which end up in the final 304 response. A proxy cache which implements this optimization can sometimes generate a legal 304 response even if it has not cached the variant data itself. ๐Ÿง 

Here's an example of a choice response ๐Ÿ‘‡

HTTP/1.1 200 OK
Date: Tue, 11 Jun 1996 20:05:31 GMT
TCN: choice
Content-Type: text/html
Last-Modified: Mon, 10 Jun 1996 10:01:14 GMT
Content-Length: 5327
Cache-control: max-age=604800
Content-Location: paper.1
Alternates: {"paper.1" 0.9 {type text/html} {language en}},
            {"paper.2" 0.7 {type text/html} {language fr}},
            {"paper.3" 1.0 {type application/postscript}
                {language en}}
Etag: "gonkyyyy;1234"
Vary: negotiate, accept, accept-language
Expires: Thu, 01 Jan 1980 00:00:00 GMT

<title>A paper about ....

10.3. Adhoc response

An adhoc response can be sent by an origin server as an extreme measure ๐Ÿšจ, to achieve compatibility with a non-negotiating or buggy client if this compatibility cannot be achieved by sending a list or choice response. There are very few requirements on the contents of an adhoc response. An adhoc response MUST have a TCN header which specifies the "adhoc" response-type, and a Vary header if the response is cacheable. It MAY contain the Alternates header bound to the negotiable resource. ๐ŸŽญ

Any Vary header in the response SHOULD ensure correct handling by plain HTTP/1.1 caching proxies. This header can either be

Vary: *

or a more elaborate header, see section 10.6.1. Depending on the status code, an adhoc response is cacheable unless indicated otherwise. ๐ŸŒท

As an example of the use of an adhoc response, suppose that the variant resource "redirect-to-blah" yields redirection (302) responses. A choice response with this variant could look as follows:

HTTP/1.1 302 Moved Temporarily
Date: Tue, 11 Jun 1996 20:02:28 GMT
TCN: choice
Content-location: redirect-to-blah
Location: http://blah.org/
Content-Type: text/html
Content-Length: 62

This document is available <a href=http://blah.org/>here</a>.

Suppose that the server knows that the receiving user agent has a bug, which causes it to crash on responses which contain both a Content-Location and a Location header. ๐Ÿ˜ฑ The server could then work around this bug by performing a server-side override and sending the following adhoc response instead:

HTTP/1.1 302 Moved Temporarily
Date: Tue, 11 Jun 1996 20:02:28 GMT
TCN: adhoc, keep
Location: http://blah.org/
Content-Type: text/html
Content-Length: 62

This document is available <a href=http://blah.org/>here</a>.

10.4. Reusing the Alternates header โœจ

Okay so, if a proxy cache has a negotiated response that's cacheable, fresh, and has ETag and Alternates headers, it MAY pull out the Alternates header and its variant list validator ๐Ÿฆ‹ and reuse them (without unnecessary delay) to negotiate on behalf of the user agent (section 13) or to build a choice response (section 10.2). The age of that extracted Alternates header is the age of the response it came from, calculated with the rules in the HTTP/1.1 specification [1]. Main character caching energy ๐Ÿ’…

10.5. Extracting a normal response from a choice response ๐ŸŽ€

If a proxy gets a choice response, it MAY extract and cache the normal HTTP response inside it. To get the normal response, you take a copy of the choice response, delete any Content-Location, Alternates, and Vary headers, rename any Variant-Vary headers to Vary headers, and shorten the structured entity tag in any ETag header to a normal entity tag. Super simple once you see it ๐Ÿงš

This normal response MAY be cached (as an HTTP response to the variant request built in step 1. of section 10.2) and reused to answer future direct requests on the variant resource, following the rules in the HTTP/1.1 specification [1] ๐ŸŒธ

Note: Caching extracted responses can cut upstream bandwidth usage by up to a factor 2, because two independent HTTP/1.1 cache entries, one tied to the negotiable resource URI and one tied to the variant URI, get created in the same transaction. Without this optimization, you'd need to transmit the variant data twice to create both HTTP/1.1 cache entries. Iconic efficiency ๐Ÿ”ฅ

For security reasons (see section 14.2), an extracted normal response MUST NEVER be cached if it belongs to a non-neighboring variant resource. If the choice response claims to contain data for a non-neighboring variant resource, the proxy SHOULD reject the choice response as a probable spoofing attempt. Not me trying to get spoofed ๐Ÿ’€

10.6. Elaborate Vary headers ๐ŸŒˆ

If an HTTP/1.1 [1] server can generate varying responses for a request on some resource, then the server MUST include a Vary header in these responses if they're cacheable. This Vary header is basically a big flashing sign for HTTP/1.1 caches saying "something special is going on here" ๐Ÿšจ It stops the caches from handing back the currently chosen response for every future request on the resource.

Servers doing transparent content negotiation will generate varying responses, so cacheable list, choice, and adhoc responses MUST always include a Vary header. No exceptions bestie ๐Ÿซถ

The simplest Vary header you can include is:

Vary: *

This one leaves the way the response gets selected by the server completely unspecified ๐Ÿคทโ€โ™€๏ธ

A more elaborate Vary header MAY be used to allow certain optimizations in HTTP/1.1 caches that don't have any specific optimizations for transparent content negotiation, but that do cache multiple variant responses for one resource. A more elaborate Vary header like this lists all the request headers the server can use when selecting a response for a request on the resource. Lowkey the best kind ๐Ÿ’œ

10.6.1. Construction of an elaborate Vary header ๐Ÿง

Origin servers can build a more elaborate Vary header like this. First, start with the header:

Vary: negotiate

negotiate is always included, because servers use the information in the Negotiate header when choosing between a list, choice, or adhoc response ๐Ÿ“

Then, if any of the following attributes shows up in any variant description in the Alternates header, add the matching header name to the Vary header:

attribute  |   header name to add
-----------+---------------------
 type      |   accept
 charset   |   accept-charset
 language  |   accept-language
 features  |   accept-features

The Vary header built this way describes the response variation that can be caused by a variant selection algorithm in proxies. If the origin server will, in some cases (for example, when contacted by a non-negotiating user agent), use a custom negotiation algorithm that takes additional headers into account, the names of those headers SHOULD also be added to the Vary header ๐ŸŽ€

10.6.2. Caching of an elaborate Vary header ๐Ÿฆ‹

A proxy cache can't build an elaborate Vary header with the method above, because that method needs exact knowledge of any custom algorithms in the origin server. Still, when extracting an Alternates header from a response (section 10.4), caches MAY also extract the Vary header from that response and reuse it along with the Alternates header. A clean Vary header can only be extracted if the variant doesn't vary itself, meaning a Variant-Vary header is absent. Ngl that's a pretty specific vibe ๐Ÿ™ˆ

10.7. Adding an Expires header for HTTP/1.0 compatibility ๐Ÿ•ฐ๏ธ

To keep things working with HTTP/1.0 caching proxies that don't recognize the Vary header, an Expires header with a date in the past can be added to the response, for example:

Expires: Thu, 01 Jan 1980 00:00:00 GMT

If an origin server does this, it SHOULD usually also include a Cache-Control header for the benefit of HTTP/1.1 caches, for example:

Cache-Control: max-age=604800

That Cache-Control header overrides the freshness lifetime of zero seconds specified by the included Expires header. Plot twist, the Cache-Control wins ๐Ÿ‘‘

Note: This specification only claims downwards compatibility with the HTTP/1.0 proxy caches that implement the HTTP/1.0 specification [2]. Some legacy proxy caches that return the HTTP/1.0 protocol version number don't honor the HTTP/1.0 Expires header as specified in [2]. Methods for achieving compatibility with those proxy caches are out of scope for this specification. Some caches just be out here doing their own thing ๐Ÿ’€

10.8. Negotiation on content encoding ๐Ÿซง

Negotiating on the content encoding of a response is orthogonal to transparent content negotiation. The rules for when a content encoding may be applied are the same as in HTTP/1.1: servers MAY content-encode responses that are the result of transparent content negotiation whenever an Accept-Encoding header in the request allows it. When negotiating on the content encoding of a cacheable response, servers MUST add the accept-encoding header name to the Vary header of the response, or add Vary: *. Two separate things, don't mix them up ๐Ÿ’…

Servers SHOULD always be able to provide unencoded versions of every transparently negotiated response. This means, in particular, that every variant in the variant list SHOULD at least be available in an unencoded form. Always have a plain backup, periodt โœจ

Like HTTP/1.1, this specification lets proxies encode or decode relayed or cached responses on the fly, unless a Cache-Control directive explicitly forbids it. The encoded or decoded response still contains the same variant as far as transparent content negotiation is concerned. Note that HTTP/1.1 requires proxies to add a Warning header if the encoding of a response is changed ๐ŸŽ€


11. User agent support for transparent negotiation ๐Ÿงš

This section lays out the requirements a user agent needs to meet in order to support transparent negotiation. If the user agent has an internal cache, that cache MUST conform to the rules for proxy caches in section 13 ๐Ÿซถ

11.1. Handling of responses ๐Ÿ’Œ

If a list response is received when a resource is accessed, the user agent MUST be able to automatically choose, retrieve, and display the best variant, or display an error message if none of the variants are acceptable. Pick the best one or tell me why not ๐Ÿ’–

If a choice response is received when a resource is accessed, the usual move is to automatically display the enclosed entity. However, if a remote variant selection algorithm was enabled and could have made a different choice from the one the local algorithm would make, the user agent MAY apply its local algorithm to any variant list in the response. If the local algorithm picks something else, it can automatically retrieve and display another variant ๐Ÿ”ฅ

When receiving a choice response, a user agent SHOULD check whether the variant resource is a neighboring variant resource of the negotiable resource. If it isn't, the user agent SHOULD reject the choice response as a probable spoofing attempt and display an error message, for example by internally replacing the choice response with a 502 (bad gateway) response. Spoofing gets absolutely nothing from me ๐Ÿ˜ค

11.2. Presentation of a transparently negotiated resource ๐Ÿฆ‹

If the user agent is displaying a variant that isn't an embedded or inlined object and that is the result of transparent content negotiation, the following requirements apply:

  1. The user agent SHOULD allow the user to review a list of all variants bound to the negotiable resource, and to manually retrieve another variant if they want. There are two general ways to provide that list. First, the information in the Alternates header of the negotiable resource could be used to make an annotated menu of variants. Second, the entity included in a list response of the negotiable resource could be displayed. Note that a list response can be obtained by doing a GET request that only has the "trans" directive in the Negotiate header ๐Ÿ“
  2. The user agent SHOULD make some indication available through its user interface that the resource being displayed is a negotiated resource instead of a plain resource. It SHOULD also let the user examine the variant list included in the Alternates header. This kind of notification and review mechanism is needed because of privacy considerations, see section 14.1 ๐Ÿ™ˆ
  3. If the user agent shows the URI of the displayed information to the user, it SHOULD be the negotiable resource URI, not the variant URI. This encourages third parties who want to refer to the displayed information in their own documents to link to the negotiable resource as a whole, rather than to the variant resource that happens to be shown. Correct linking like this is vital for interoperability of content across sites ๐ŸŒธ The user agent SHOULD still also provide a means for reviewing the URI of the particular variant currently being displayed.
  4. Similarly, if the user agent stores a reference to the displayed information for future use (for example, in a hotlist), it SHOULD store the negotiable resource URI, not the variant URI ๐Ÿ’œ

It is encouraged, but not required, that some of the above functionality is also made available for inlined or embedded objects, and when a variant that was selected manually is being displayed โœจ

12. Origin server support for transparent negotiation ๐ŸŒท

12.1 Requirements ๐Ÿ’…

okay so if you want transparent negotiation on a resource, the origin server MUST be able to send a list response whenever it gets a GET request on that resource ๐Ÿงš. It SHOULD also be able to send appropriate list responses for HEAD requests ๐Ÿ’–. And when a request hits a transparently negotiable resource, the origin server MUST NEVER return a response with a 2xx status code or any 3xx status code, except 304, which is not a list, choice, or adhoc response ๐Ÿ’€.

if the request has a Negotiate header with a "vlist" or "trans" directive, but no directive that lets the server pick a best variant, a list response MUST ALWAYS be sent, except when the server is doing a server-side override for bug compatibility ๐Ÿ™ˆ. if the request has a Negotiate header with a "vlist" or "guess-small" directive, an Alternates header with the variant list bound to the negotiable resource MUST ALWAYS be sent in any list, choice, or adhoc response, except when the server is doing a server-side override for bug compatibility โœจ.

if the Negotiate header allows it, the origin server MAY run a remote variant selection algorithm. if that algorithm has enough info to pick a best variant, and the best variant is a neighboring variant, the origin server MAY return a choice response with this variant ๐Ÿฆ‹.

when a request on a transparently negotiable resource comes from a user agent that doesn't support transparent content negotiation, the origin server MAY use a custom algorithm to pick between sending a list, choice, or adhoc response ๐ŸŒธ.

here's the table that sums up all these rules ๐Ÿ“‹

   |Req on   |Usr agnt|server-  |         Response may be:         |
   |trans neg|capable |side     +------+------+------+------+------+
   |resource?|of TCN? |override?|list  |choice|adhoc |normal|error |
   +---------+--------+---------+------+------+------+------+------+
   |   Yes   |  Yes   |  No     |always|smt(*)|never |never |always|
   +---------+--------+---------+------+------+------+------+------+
   |   Yes   |  Yes   |  Yes    |always|always|always|never |always|
   +---------+--------+---------+------+------+------+------+------+
   |   Yes   |  No    |   -     |always|always|always|never |always|
   +---------+--------+---------+------+------+------+------+------+
   |   No    |   -    |   -     |never |never |never |always|always|
   +---------+--------+---------+------+------+------+------+------+
      (*) sometimes, when allowed by the Negotiate request header

negotiability is a binary thing: a resource is either transparently negotiated, or it isn't ๐ŸŽ€. Origin servers SHOULD NOT vary the negotiability of a resource, or the variant list bound to that resource, based on the request headers they receive. The variant list and the property of being negotiated MAY however change through time. The Cache-Control header can be used to control how those time-dependent changes spread through caches ๐ŸŒˆ.

it's the author of the negotiable resource's job to make sure all resources in the variant list serve the intended content, and that the variant resources don't do transparent content negotiation themselves ๐Ÿ’….


12.2 Negotiation on transactions other than GET and HEAD ๐ŸŽ€

if a resource is transparently negotiable, that only affects the GET and HEAD transactions on the resource. it's not possible (under this specification) to do transparent content negotiation on the direct result of a POST request ๐Ÿ™ˆ.

however, a POST request can return an unnegotiated 303 (See Other) response that makes the user agent do a GET request on a second resource. that second resource can then use transparent content negotiation to return an appropriate final response โœจ. the figure below shows this ๐Ÿ‘‡

   Server ______ proxy ______ proxy ______ user
   x.org         cache        cache        agent

     < -------------------------------------
     |     POST http://x.org/cgi/submit
     |     <form contents in request body>
     |
     -------------------------------------- >
           303 See Other                    |
           Location: http://x.org/result/OK |
                                            |
     < -------------------------------------
     |     GET http://x.org/result/OK
     |      small Accept- headers
     |
   able to choose on
   behalf of user agent
     |
      ------------------------------------- >
           choice response with             |
           ..result/OK.nl variant           |
                                        displays OK.nl

see the HTTP/1.1 specification [1] for details on the 303 (See Other) status code. note that this status code is not understood by some HTTP/1.0 clients ๐Ÿ’€.


13 Proxy support for transparent negotiation ๐Ÿงš

transparent content negotiation is an extension on top of HTTP/1.x. it's designed to work through any proxy that only implements the HTTP/1.1 specification [1]. if Expires headers are added as discussed in section 10.7, negotiation will also work through proxies that implement HTTP/1.0 [2]. so every HTTP/1.0 or HTTP/1.1 proxy gives support for transparent content negotiation ๐ŸŒท. however, if an HTTP/1.x proxy wants to claim it offers transparent content negotiation services, at least one of the specific optimizations below MUST be implemented ๐Ÿ‘‘.

an HTTP/1.x proxy MUST ONLY optimize (change) the HTTP traffic flowing through it in ways that are explicitly allowed by the specification(s) it conforms to. a proxy that supports transparent content negotiation on top of HTTP/1.x MAY perform the optimizations allowed for by HTTP/1.x. on top of that, it MAY perform three additional optimizations, defined below, on the HTTP traffic for transparently negotiated resources and their neighboring variant resources ๐Ÿฆ‹.

first, when a request on a transparently negotiable resource comes from a user agent that supports transparent content negotiation, the proxy MAY return any cached, fresh list response from that resource, even if the selecting request headers, as specified by the Vary header, don't match ๐Ÿ’–.

second, when the user agent and origin server allow it, a proxy MAY reuse an Alternates header taken from a previous response (section 10.4) to run a remote variant selection algorithm. if the algorithm has enough info to pick a best variant, and that best variant is a neighboring variant, the proxy MAY return a choice response with this variant ๐Ÿ”ฅ.

third, if a proxy receives a choice response, it MAY extract and cache the normal response embedded in it, as described in section 10.5 ๐Ÿซถ.


14 Security and privacy considerations ๐Ÿ”

14.1 Accept- headers revealing personal information ๐Ÿ™ˆ

Accept- headers, especially Accept-Language headers, can reveal info the user would rather keep private, unless sending it will directly improve the quality of service. for example, a user might not want to send language preferences to sites that don't offer multi-lingual content. the transparent content negotiation mechanism lets user agents leave out the Accept-Language header by default, without messing up the negotiation outcome if transparently negotiated multi-lingual content is accessed ๐ŸŒธ.

however, even if Accept- headers are never sent, the automatic selection and retrieval of a variant by a user agent will still reveal a preference for that variant to the server. a malicious service author could make a page with `fake' negotiability on (ethnicity-correlated) languages, where every variant is actually the same English document, just to collect privacy-sensitive info ๐Ÿ˜ญ. but an alert victim would be able to spot that plot if they reviewed the list of available variants and their properties ๐Ÿ‘€.

some additional privacy considerations connected to Accept- headers are discussed in [1].

14.2 Spoofing of responses from variant resources ๐Ÿšจ

bestie, this is the one to read twice ๐Ÿšจ

the caching optimization in section 10.5 gives the implementer of a negotiable resource control over the responses cached for all neighboring variant resources. that's a security problem if a neighboring variant resource belongs to another author. to provide security in this case, the HTTP server will have to filter the Content-Location headers in the choice responses generated by the negotiable resource implementation ๐Ÿ›ก๏ธ.

14.3 Security holes revealed by negotiation ๐Ÿ’€

malicious servers could use transparent content negotiation to find out about security holes that may be present in user agents. this is a particular risk for negotiation on the availability of scripting languages and libraries ๐Ÿ˜ฑ.


15 Internationalization considerations ๐ŸŒ

this protocol defines negotiation facilities that can be used for the internationalization of web content. for the internationalization of list response bodies (section 10.1), HTTP/1.0 style negotiation (section 4.2) can be used ๐ŸŒ.


16 Acknowledgments ๐Ÿซถ

work on HTTP content negotiation has been going on since at least 1993. the authors can't trace the origin of many of the ideas in this document. lots of members of the HTTP working group helped shape the negotiation model in this specification. the authors wanna thank everyone who commented on earlier versions of this document, including Brian Behlendorf, Daniel DuBois, Martin J. Duerst, Roy T. Fielding, Jim Gettys, Yaron Goland, Dirk van Gulik, Ted Hardie, Graham Klyne, Scott Lawrence, Larry Masinter, Jeffrey Mogul, Henrik Frystyk Nielsen, Frederick G.M. Roeber, Paul Sutton, and Klaus Weide and Mark Wood ๐Ÿ’œ.


17 References ๐Ÿ“š

[1] Fielding, R., Gettys, J., Mogul, J., Frystyk, H., and T. Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2068, January 1997.

[2] Berners-Lee, T., Fielding, R., and H. Frystyk, "Hypertext Transfer Protocol -- HTTP/1.0", RFC 1945, May 1996.

[3] Holtman, K., and A. Mutz, "HTTP Remote Variant Selection Algorithm -- RVSA/1.0", RFC 2296, March 1998.

[4] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.

[5] Yergeau, F., "UTF-8, a transformation format of Unicode and ISO 10646", RFC 2044, October 1996.


18 Authors' Addresses โœ๏ธ

Koen Holtman
Technische Universiteit Eindhoven
Postbus 513
Kamer HG 6.57
5600 MB Eindhoven (The Netherlands)

EMail: koen@win.tue.nl


Andrew H. Mutz
Hewlett-Packard Company
1501 Page Mill Road 3U-3
Palo Alto CA 94304, USA

Fax +1 415 857 4691
EMail: mutz@hpl.hp.com

19 Appendix: Example of a local variant selection algorithm ๐Ÿง 

a negotiating user agent will choose the best variant from a variant list with a local variant selection algorithm. this appendix contains an example of such an algorithm.

the inputs of the algorithm are a variant list from an Alternates header, and an agent-side configuration database, which contains

  • the feature set of the current request,
  • a collection of quality values assigned to media types, languages, and charsets for the current request, following the model of the corresponding HTTP/1.1 [1] Accept- headers,
  • a table which lists `forbidden' combinations of media types and charsets, i.e. combinations which cannot be displayed because of some internal user agent limitation.

the output of the algorithm is either the best variant, or the conclusion that none of the variants are acceptable ๐Ÿ’–

19.1 Computing overall quality values

okay bestie, step one of the local variant selection algorithm is to work out the overall quality of every variant description in the list โœจ

the overall quality Q of a variant description is:

Q = round5( qs * qt * qc * ql * qf * qa )

round5 is a function that rounds a floating point value to 5 decimal places ๐Ÿ’– we assume the user agent can run on multiple platforms, so that rounding keeps the algorithm from caring about the exact floating point hardware underneath ๐Ÿซถ

here's how each of the six factors works ๐Ÿ’…

  • qs is the source quality factor in the variant description.
  • qt is the media type quality factor. it's 1 if there's no type attribute in the variant description. otherwise it's the quality value the configuration database assigns to that type. if the database doesn't assign a value, the factor is 0.
  • qc is the charset quality factor. it's 1 if there's no charset attribute in the variant description. otherwise it's the quality value the configuration database assigns to that charset. if the database doesn't assign a value, the factor is 0.
  • ql is the language quality factor. it's 1 if there's no language attribute in the variant description. otherwise it's the highest quality value the configuration database assigns to any of the languages listed in the language attribute. if the database doesn't assign a value to any of those languages, the factor is 0.
  • qf is the features quality factor. it's 1 if there's no features attribute in the variant description. otherwise it's the quality degradation factor computed for the features attribute, using the feature set of the current request.
  • qa is the quality adjustment factor. it's 0 if the variant description lists a media type - charset combination which is `forbidden' by the table, and 1 otherwise.

let's do an example ๐ŸŽ€ say a variant list contains this variant description:

{"paper.2" 0.7 {type text/html} {language fr}}

and the configuration database has these quality value assignments:

types:     text/html;q=1.0, type application/postscript;q=0.8
languages: en;q=1.0, fr;q=0.5

then the local variant selection algorithm works out the overall quality for that description like this:

     {"paper.2" 0.7 {type text/html} {language fr}}
                 |           |                 |
                 |           |                 |
                 V           V                 V
       round5 ( 0.7   *     1.0        *      0.5 ) = 0.35000

with the same configuration database, this variant list:

{"paper.1" 0.9 {type text/html} {language en}},
{"paper.2" 0.7 {type text/html} {language fr}},
{"paper.3" 1.0 {type application/postscript} {language en}}

would give these computations ๐Ÿงฎ

     round5 ( qs  * qt  * qc  * ql  * qf  * qa ) = Q
              ---   ---   ---   ---   ---   ---
    paper.1:  0.9 * 1.0 * 1.0 * 1.0 * 1.0 * 1.0  = 0.90000
    paper.1:  0.7 * 1.0 * 1.0 * 0.5 * 1.0 * 1.0  = 0.35000
    paper.3:  1.0 * 0.8 * 1.0 * 1.0 * 1.0 * 1.0  = 0.80000

19.2 Determining the result

okay so once every overall quality value is computed, the final result of the local variant selection algorithm comes down to a couple of rules ๐Ÿ‘‘

if all the overall quality values are 0, the best variant is the fallback variant, as long as there is one in the list. if there isn't one, the result is the conclusion that none of the variants are acceptable ๐Ÿ˜ญ

if at least one overall quality value is greater than 0, the best variant is the one whose description has the highest overall quality value. if several variant descriptions share that highest value, the best variant is the one from the first of those descriptions in the list โœจ

19.3 Ranking dimensions

let's talk about ranking dimensions, which is a little bit of a brain teaser ๐Ÿง  consider this variant list:

{"paper.greek"   1.0 {language el} {charset ISO-8859-7}},
{"paper.english" 1.0 {language en} {charset ISO-8859-1}}

it could totally happen that the user prefers the language "el" over "en", while the user agent can render "ISO-8859-1" better than "ISO-8859-7" ๐ŸŒธ so in the language dimension the first variant is best, but in the charset dimension the second variant is best. in this situation, we'd rather pick the first variant, because the user's settings in the language dimension should take precedence over the hard-coded values in the charset dimension. that's giving priority energy ๐Ÿ’œ

to express this ranking between dimensions, the user agent configuration database should have a higher spread in its quality values for the language dimension than for the charset dimension. for example, with these values:

languages: el;q=1.0, en-gb;q=0.7, en;q=0.6, da;q=0, ...

charsets:  ISO-8859-1;q=1.0, ISO-8859-7;q=0.95,
           ISO-8859-5;q=0.97, unicode-1-1;q=0, ...

the first variant ends up with an overall quality of 0.95000, while the second variant ends up with an overall quality of 0.70000. so the first variant becomes the best variant ๐Ÿฆ‹


20 Appendix: feature negotiation examples

this appendix is full of examples of feature tags in variant descriptions ๐Ÿงš just a heads up: the tag names used here are only examples. they don't in general reflect the tag naming scheme proposed in [4].

20.1 Use of feature tags

feature tags can be used in variant lists to express the quality degradation that comes with the presence or absence of certain features ๐Ÿ’… one example:

{"index.html.plain" 0.7 },
{"index.html"       1.0 {features tables frames}}

here, the {features tables frames} part says that index.html uses the features tagged as tables and frames. if those features are absent, the overall quality of index.html degrades to 0. another example:

{"home.graphics" 1.0 {features !textonly}},
{"home.textonly" 0.7 }

in this one, the {features !textonly} part says that home.graphics requires the absence of the textonly feature. if that feature is present, the overall quality of home.graphics degrades to 0 ๐Ÿ™ˆ

the absence of a feature doesn't always have to degrade the overall quality all the way to 0 โœจ in this example:

{"x.html.1" 1.0 {features fonts;-0.7}}

the absence of the fonts feature degrades the quality with a factor of 0.7. and finally:

{"y.html" 1.0 {features [blebber wolx] }}

the [blebber wolx] part says that y.html requires the presence of the blebber feature or the wolx feature. this construct comes in handy in a few situations ๐ŸŒˆ

  1. blebber and wolx actually tag the same feature, but different people registered them, and some user agents say they support blebber while others say they support wolx.
  2. blebber and wolx are HTML tags from different vendors that implement the same functionality, and they're used together in y.html without interfering with each other.
  3. blebber and wolx are HTML tags from different vendors that implement the same functionality, and y.html uses the tags inside a conditional HTML construct.
  4. blebber is a complicated HTML tag with only a sketchy definition, implemented by one user agent vendor, and wolx indicates implementation of a well-defined subset of the blebber tag by some other vendor(s). y.html uses only that well-defined subset.

20.2 Use of numeric feature tags

for negotiation in a numeric area, check out this variant list, which describes four variants with title graphics designed for increasing screen widths ๐Ÿ“ฑ

{"home.pda"    1.0 {features screenwidth=[-199] }},
{"home.narrow" 1.0 {features screenwidth=[200-599] }},
{"home.normal" 1.0 {features screenwidth=[600-999] }},
{"home.wide"   1.0 {features screenwidth=[1000-] }},
{"home.normal"}

the last element of the list is a safe default for user agents that don't implement screen width negotiation ๐Ÿซถ those user agents will reject the first four variants as unusable, because those variants seem to rely on a feature they don't understand.

20.3 Feature tag design

when you design a new feature tag, you have to keep something important in mind ๐ŸŽ€ existing user agents that don't recognize the new tag will treat the feature as absent. so in general, a new feature tag needs to be designed so that absence of the tag is the default case, which reflects current practice. if you ignore this design principle, the resulting feature tag will generally be unusable ๐Ÿ’€

for example, you could try to support negotiation between monochrome and color content by introducing a `color' feature tag, whose presence would indicate the capability to display color graphics. but if this new tag is used in a variant list like this:

{"rainbow.gif"      1.0 {features color} }
{"rainbow.mono.gif" 0.6 {features !color}}

then existing user agents, which wouldn't recognize the color tag, would all display the monochrome rainbow ๐ŸŒˆ so the color tag is unusable wherever you want optimal results for existing user agents. to set up negotiation in this area, you'd have to introduce a `monochrome' feature tag instead. its presence indicates that the user agent can only render (or the user prefers to view) monochrome graphics โœจ


21 Appendix: origin server implementation considerations

21.1 Implementation with a CGI script

transparent content negotiation was designed to allow a broad range of implementation options on the origin server side ๐ŸŒธ a very minimal implementation can be done using the CGI interface, and the CGI script below is an example:

#!/bin/sh

cat - <<'blex'
TCN: list
Alternates: {"stats.tables.html" 1.0 {type text/html} {features
tables}}, {"stats.html" 0.8 {type text/html}}, {"stats.ps" 0.95
{type application/postscript}}
Vary: *
Content-Type: text/html

<title>Multiple Choices for Web Statistics</title>
<h2>Multiple Choices for Web Statistics:</h2>
<ul>
<li><a href=stats.tables.html>Version with HTML tables</a>
<p>
<li><a href=stats.html>Version without HTML tables</a>
<p>
<li><a href=stats.ps>Postscript version</a>
</ul>
blex
the Alternates header in the script above must be read as a single line ๐Ÿ™ˆ and the script always generates a list response with the 200 (OK) code, which keeps it compatible with non-negotiating HTTP/1.0 agents.

21.2 Direct support by HTTP servers

sophisticated HTTP servers could make a transparent negotiation module available to content authors ๐Ÿ’– that kind of module could include a remote variant selection algorithm, plus an implementation of the algorithm for generating choice responses (section 10.2). defining the interfaces to such modules is beyond the scope of this specification, so that's a problem for another day ๐Ÿงš

21.3 Web publishing tools

web publishing tools could automatically generate several variants of a document ๐Ÿฆ‹ for example, the original TeX version, an HTML version with tables, an HTML version without tables, and a Postscript version, all together with an appropriate variant list in the interface format of an HTTP server transparent negotiation module. that would let documents be published as transparently negotiable resources, which is honestly iconic ๐Ÿ‘‘

22. Appendix: Example of choice response construction โœจ

okay so this is the full example ๐ŸŽ€ a proxy cache building a choice response, with HTTP/1.1 and transparent content negotiation, and it also shows off the conditional request stuff. so iykyk ๐Ÿ’…

Okay, the setup. A user agent has cached a variant list with the validator "1234" for the negotiable resource http://x.org/paper. It also has cached responses from two neighboring variants, with the entity tags "gonkyyyy" and W/"a;b". All three of those user agent cache entries are stale, so they need to be revalidated before the user agent can use them ๐Ÿฅบ. If someone accesses http://x.org/paper in this situation, the user agent could send this request to its proxy cache:

GET /paper HTTP/1.1
Host: x.org
User-Agent: WuxtaWeb/2.4
Negotiate: 1.0
Accept: text/html, application/postscript;q=0.4, */*
Accept-Language: en
If-None-Match: "gonkyyyy;1234", W/"a;b;1234"

Now the proxy has cached the same three items as the user agent, but it revalidated the variant list 8000 seconds ago, so the list is still fresh for the proxy ๐Ÿงš. That means the proxy can run a remote variant selection algorithm on the list and the incoming request. main character energy for the proxy honestly ๐Ÿ‘‘

Say the remote algorithm picks paper.html.en as the best variant. The proxy can now build a choice response using the algorithm in section 10.2. In steps 1 and 2 of that algorithm, the proxy constructs this conditional request on the best variant and sends it to the origin server:

GET /paper.html.en HTTP/1.1
Host: x.org
User-Agent: WuxtaWeb/2.4
Negotiate: 1.0
Accept: text/html, application/postscript;q=0.4, */*
Accept-Language: en
If-None-Match: "gonkyyyy", W/"a;b"
Via: 1.1 fred

The origin server responds with this, and it's giving "nothing changed" vibes ๐Ÿ˜ญ

HTTP/1.1 304 Not Modified
Date: Tue, 11 Jun 1996 20:05:31 GMT
Etag: "gonkyyyy"

So now the proxy can use its freshly revalidated paper.html.en cache entry to expand that into a non-304 response ๐ŸŒธ:

HTTP/1.1 200 OK
Date: Tue, 11 Jun 1996 20:05:31 GMT
Content-Type: text/html
Last-Modified: Mon, 10 Jun 1996 10:01:14 GMT
Content-Length: 5327
Cache-control: max-age=604800
Etag: "gonkyyyy"
Via: 1.1 fred
Age: 0

<title>A paper about ....

Using that 200 response, the proxy builds the choice response in step 4 of the algorithm. this is the iconic one ๐Ÿ’–

HTTP/1.1 200 OK
Date: Tue, 11 Jun 1996 20:05:31 GMT
TCN: choice
Content-Type: text/html
Last-Modified: Mon, 10 Jun 1996 10:01:14 GMT
Content-Length: 5327
Cache-control: max-age=604800
Content-Location: paper.html.en
Alternates: {"paper.html.en" 0.9 {type text/html} {language en}},
            {"paper.html.fr" 0.7 {type text/html} {language fr}},
            {"paper.ps.en"   1.0 {type application/postscript}
                {language en}}

Etag: "gonkyyyy;1234"
Vary: negotiate, accept, accept-language
Expires: Thu, 01 Jan 1980 00:00:00 GMT
Via: 1.1 fred
Age: 8000

<title>A paper about ....

The choice response can then get shortened to a 304 response, because the original request from the user agent had an If-None-Match header ๐Ÿ™ˆ. so the proxy can finally return this to the user agent:

HTTP/1.1 304 Not Modified
Date: Tue, 11 Jun 1996 20:05:31 GMT
Etag: "gonkyyyy;1234"
Content-Location: paper.html.en
Vary: negotiate, accept, accept-language
Expires: Thu, 01 Jan 1980 00:00:00 GMT
Via: 1.1 fred
Age: 8000

and that's the whole glow-up arc, bestie ๐Ÿฆ‹โœจ


Copyright (C) The Internet Society (1998). All Rights Reserved. ๐Ÿ’œ

Okay, the legal stuff, but make it cute. You can copy this document and hand it to other people, and you can make derivative works that comment on or explain it or help people implement it. Those can be prepared, copied, published, and distributed, in whole or in part, with no restrictions at all ๐ŸŒˆ, as long as the copyright notice and this paragraph show up on every copy and derivative work.

BUT the document itself can't be modified in any way. That includes removing the copyright notice or the references to the Internet Society or other Internet organizations ๐Ÿ™…. The one exceptions are when you need it for developing Internet standards (then you have to follow the copyright procedures defined in the Internet Standards process), or when you need to translate it into a language other than English.

The limited permissions above are perpetual. The Internet Society and its successors or assigns will never revoke them โœจ.

The last bit is the disclaimer, and it's serious, so it's in caps like it means it:

This document and the information contained herein is provided on an "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. ๐Ÿ’€
๐Ÿ“ฆ where this page comes from