Balancing what's acceptable with what's possible

Content Negotiation

Preliminaries

This note describes how browsers and servers proclaim their ability to handle different file formats, allowing the browser to request and receive only files that it is able to understand.

Browser capabilities evolve over time, with the deprecation of old file formats and the introduction of new file formats. Servers learn about these changes through the accept-type header which the browser sends with each request. If the server has a file in a format that is acceptable to the browser, it responds with a content-type header. When an acceptable type is not available, the server responds with status code 406.

By way of example, some versions of Internet Explorer used to send request headers that included the value image/x-windows-bmp, Microsoft Paint's bitmap file format. On the other hand, some newer versions of Chrome have begun sending headers that include image/webp, Google's new WebP file format.

In this exchange of information, file formats are identified using media-types, also known as MIME types or content-types. These are described in IETF RFC 6838 Media Type Specifications and Registration Procedures.

Browsers can properly read and render many different file types, depending on the context of the request. When an anchor tag is clicked, a request might include an accept-type like text/html,application/xhtml+xml,application/xml,*/* indicating to the server that it can handle HTML, XHTML, XML or anything else. Alternatively, when an image tag is encountered, a request might include an accept-type like image/webp,image/apng,image/* indicating that it can handle WebP, animated PNG, or any other image type. Or, when a link tag referencing an external stylesheet is encountered, a request might include an accept-type like text/css,*/* indicating preference for a CSS file, but fallback acceptance of any file type.

Configuration

The server side of this negotiation is configured using the accept-types configuration section, which comprises a list of mime-type entries, each entry specifying a single RFC 6838 media type.

Each MIME type is composed of a media type and a subtype. Media types include: text, application, image, audio, video and multipart. Subtypes include values like: html, javascript, jpeg, mp3, mp4 and so forth.

When configuring the server, an asterisk may be used for the subtype, to indicate that any file format defined under that media type can be served. For example, a value of text/* could be configured to indicate that all requests having an accept-type with a media type of text will be honored; this would include the wide variety of file formats including: text/html, text/plain, text/css, etc.

The special configuration value '*/*' may also be used (as the last entry in the list) to indicate that any file type will be served.

When preparing a response, the server decides whether to respond with a status code of 200 or 406 using this pattern:

Request Header Server Configuration Status Code
image/webp image/webp 200
image/* image/webp 200
*/* image/webp 200
image/webp image/* 200
image/webp */* 200
image/webp --- 406

Placement

The accept-types configuration section may appear in either the server section or a host section. When values occur in both the server and host sections, they are merged according to the standard rules defined for the merge attribute.

Information Headers

When a user-agent's request cannot be fulfilled because there is no acceptable type configured, the standard HTTP status code 406 is returned together with a rw-no-acceptable-type header providing additional information.

EBNF

SP ::= U+20
CR ::= U+0D
SOLIDUS ::= U+2F
ASTERISK ::= U+2A
LEFT-CURLY-BRACKET ::= U+7B
RIGHT-CURLY-BRACKET ::= U+7D
media-type ::= 'text' | 'application' | 'image' | 'audio' | 'video' | 'multipart'
subtype ::= (ALPHA | DIGIT | )*
MIME-type ::= (media-type | ASTERISK) SOLIDUS (subtype | ASTERISK)
mime-type-entry ::= 'mime-type' SP MIME-type CR
accept-types-section ::= 'accept-types' SP LEFT-CURLY-BRACKET CR
mime-type-entry*
RIGHT-CURLY-BRACKET CR

† See section 4.2 of RFC 6838 for exact rules

Cookbook

Example 1: All types are acceptable
server {
accept-types {
mime-type '*/*'
}
}

(Note that the special value '*/*' must be enclosed in single or double quotes so that the parser does not misinterpret the sequence as a comment opening tag.)

Example 2: All text and image types are acceptable
server {
accept-types {
mime-type text/*
mime-type image/*
}
}
Example 3: Selected text and image types are acceptable
server {
accept-types {
mime-type text/html
mime-type text/plain
mime-type text/css
mime-type image/png
mime-type image/jpeg
mime-type image/gif
mime-type image/svg+xml
mime-type image/webp
}
}
Example 4: A comprehensive configuration
server {
accept-types {
mime-type text/html
mime-type text/plain
mime-type text/css
mime-type application/xhtml+xml
mime-type application/xml
mime-type application/json
mime-type application/javascript
mime-type application/pdf
mime-type image/png
mime-type image/jpeg
mime-type image/gif
mime-type image/svg+xml
mime-type image/webp
mime-type audio/webm
mime-type audio/ogg
mime-type audio/mpeg
mime-type audio/wav
mime-type audio/flac
mime-type video/webm
mime-type video/ogg
mime-type video/mp4
}
}

Review

Key points to remember:

  • The server will respond with status code 200 for any request whose accept-type matches a configured mime-type, either exactly or through wildcards.
  • The server will respond with status code 406 and rw-no-acceptable-type information header for requests that do not match any configured mime-type.

Balancing what's acceptable with what's possible