CC0 1.0 Universal. To the extent possible under law, the editor has waived all copyright and related or neighboring rights to this work. In addition, as of 17 January 2014, the editor has made this specification available under the Open Web Foundation Agreement Version 1.0, which is available at http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
The HTTP Content-Type
header field is intended to indicate the
MIME type of an HTTP response.
However, many HTTP servers supply a Content-Type
header field
value that does not match the actual contents of the response.
Historically, web browsers have tolerated these servers by examining the
content of HTTP responses in addition to the Content-Type
header
field in order to determine the effective MIME type of the response.
Without a clear specification for how to "sniff" the MIME type, each user agent has been forced to reverse-engineer the algorithms of other user agents in order to maintain interoperability. Inevitably, these efforts have not been entirely successful, resulting in divergent behaviors among user agents. In some cases, these divergent behaviors have had security implications, as a user agent could interpret an HTTP response as a different MIME type than the server intended.
These security issues are most severe when an "honest" server allows
potentially malicious users to upload their own files and then serves the
contents of those files with a low-privilege MIME type.
For example, if a server believes that the client will treat a contributed
file as an image (and thus treat it as benign), but a user agent believes the
content to be HTML (and thus privileged to execute any scripts contained
therein), an attacker might be able to steal the user's authentication
credentials and mount other cross-site scripting attacks.
(Malicious servers, of course, can specify an arbitrary MIME type in the
Content-Type
header field.)
This document describes a content sniffing algorithm that carefully balances the compatibility needs of user agent with the security constraints imposed by existing web content. The algorithm originated from research conducted by Adam Barth, Juan Caballero, and Dawn Song, based on content sniffing algorithms present in popular user agents, an extensive database of existing web content, and metrics collected from implementations deployed to a sizable number of users. [SECCONTSNIFF]
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119. For readability, these keywords will generally not appear in all uppercase letters. [RFC2119]
Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the keyword used in introducing the algorithm.
Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, note that the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant.
A byte is a byte as defined in the Encoding Standard. When helpful, a byte will be accompanied by information about the ASCII code point it represents. [ENCODING] [ASCII]
A binary data byte is a byte in the range 0x00 to 0x08 (NUL to BS), the byte 0x0B (VT), a byte in the range 0x0E to 0x1A (SO to SUB), or a byte in the range 0x1C to 0x1F (FS to US).
A whitespace byte (0xWS) is any of the following bytes: 0x09 (HT), 0x0A (LF), 0x0C (FF), 0x0D (CR), 0x20 (SP).
A tag-terminating byte (0xTT) is any of the following
bytes: 0x20 (SP), 0x3E (">
").
A byte sequence is a sequence of bytes.
Converting a string to ASCII lowercase is accomplished by converting a string to ASCII lowercase as defined in the HTML Standard. [HTML]
The MIME type of a resource is a technical hint about the use and format of that resource. [MIMETYPE]
A parsable MIME type is a MIME type for which the parse a MIME type algorithm does not return undefined. Every parsable MIME type has a corresponding parsed MIME type, which is the result of parsing the parsable MIME type. A parsed MIME type is made up of a type, a subtype, and a dictionary of parameters.
A serialized MIME type is the result of serializing a parsed MIME type.
The MIME type portion of a parsable MIME type is the result of serializing the type and subtype of its parsed MIME type with null parameters.
The MIME type portion of a parsable MIME type excludes any and all parameters.
A parsable MIME type is supported by the user agent if the user agent has the capability to interpret a resource of that MIME type and present it to the user.
To parse a MIME type, the user agent must execute the following steps:
").
/
"), continuously execute the
following steps:
;
"), continuously execute the following steps:
;
"), exit loop
M.
"
"), execute the following
steps:
"
"), execute the following steps:
"
"),
increment s by 1.
\
") and
sequence[s + 1] is not undefined, increment
s by 1.
;
"), exit loop
N.
").
=
"), continuously execute the following
steps:
") and parameters[name]
is undefined, set parameters[name] to null.
=
"), exit loop
M.
").
"
"), execute the following
steps:
"
"),
execute the following steps:
"
"),
increment s by 1.
\
") and
sequence[s + 1] is not undefined, increment
s by 1.
;
"), execute the following steps:
The parse a MIME type algorithm is intended to be executed after any protocol-specific syntax within the MIME type has been handled.
To serialize a MIME type, given a type, a subtype, and a dictionary of parameters, execute the following steps:
"), or has a length greater than
127, return undefined.
/
"), and
subtype.
charset
" or
"codecs
" parameters first?
;
") to serialization.
=
") to serialization.
"
") to serialization.
"
") or to the
U+005C REVERSE SOLIDUS character
("\
"), append the U+005C REVERSE SOLIDUS
character ("\
") to
serialization.
"
") to serialization.
;
") to serialization.
base64
" boolean parameter last?
An image type is any parsable MIME type where
type is equal to "image
".
An audio or video type is any parsable MIME type
where type is equal to "audio
" or
"video
" or where the MIME type portion is
equal to one of the following:
application/ogg
A font type is any parsable MIME type where the MIME type portion is equal to one of the following:
application/font-ttf
application/font-cff
application/font-off
application/font-sfnt
application/vnd.ms-opentype
application/font-woff
application/vnd.ms-fontobject
A ZIP-based type is any parsable MIME type where the
subtype ends in "+zip
" or the MIME type
portion is equal to one of the following:
application/zip
An archive type is any parsable MIME type where the MIME type portion is equal to one of the following:
application/x-rar-compressed
application/zip
application/x-gzip
An XML type is any parsable MIME type where the
subtype ends in "+xml
" or the MIME type
portion is equal to "text/xml
" or
"application/xml
".
[RFC3023]
A scriptable MIME type is an XML type or any parsable MIME type where the MIME type portion is equal to one of the following:
text/html
application/pdf
A resource is ….
For each resource it handles, the user agent must keep track of the following associated metadata:
The user agent can choose to use outside information, such as previous experience with a site, to determine whether to opt out of sniffing for a particular resource. The user agent can also choose to opt out of sniffing for all resources. However, opting out of sniffing does not exempt the user agent from using the MIME type sniffing algorithm.
The supplied MIME type of a resource is provided to the user agent by an external source associated with that resource. The method of obtaining this information varies depending upon how the resource is retrieved.
To determine the supplied MIME type of a resource, user agents must use the following supplied MIME type detection algorithm:
Content-Type
headers are associated with the
resource, execute the following steps:
Content-Type
header associated with the
resource.
File extensions are not used to determine the supplied MIME type of a resource retrieved via HTTP because they are unreliable and easily spoofed.
Bytes in Hexadecimal | Bytes in ASCII |
---|---|
74 65 78 74 2F 70 6C 61 69 6E |
text/plain
|
74 65 78 74 2F 70 6C 61 69 6E 3B 20 63 68 61 72 73 65 74 3D 49 53 4F 2D 38 38 35 39 2D 31 |
text/plain; charset=ISO-8859-1
|
74 65 78 74 2F 70 6C 61 69 6E 3B 20 63 68 61 72 73 65 74 3D 69 73 6F 2D 38 38 35 39 2D 31 |
text/plain; charset=iso-8859-1
|
74 65 78 74 2F 70 6C 61 69 6E 3B 20 63 68 61 72 73 65 74 3D 55 54 46 2D 38 |
text/plain; charset=UTF-8
|
The supplied MIME type detection algorithm detects these exact byte sequences because some older installations of Apache contain a bug that causes them to supply one of these Content-Type headers when serving files with unrecognized MIME types.
X-Content-Type-Options
headers are associated
with the resource and any such
X-Content-Type-Options
header has a value equal to
"nosniff
", set the no-sniff flag.
A resource header is the byte sequence at the beginning of a resource, as determined by reading the resource header.
To read the resource header, perform the following steps:
If the number of bytes in buffer is greater than or equal to 512, the MIME type sniffing algorithm will be deterministic for the majority of cases. However, certain factors (such as a slow connection) may prevent the user agent from reading 512 bytes in a reasonable amount of time.
The resource header need only be determined once per resource.
A byte pattern is a byte sequence used as a template to be matched against in the pattern matching algorithm.
A pattern mask is a byte sequence used to determine the significance of bytes being compared against a byte pattern in the pattern matching algorithm.
In a pattern mask, 0xFF indicates the byte is strictly significant, 0xDF indicates that the byte is significant in an ASCII case-insensitive way, and 0x00 indicates that the byte is not significant.
To determine whether a byte sequence matches a particular byte pattern, use the following pattern matching algorithm:
To determine which image type byte pattern a byte sequence matches, if any, use the following image type pattern matching algorithm:
Byte Pattern | Pattern Mask | Leading Bytes to Be Ignored | Image Type | Note |
---|---|---|---|---|
00 00 01 00 | FF FF FF FF | None. |
image/x-icon
|
A Windows Icon signature. |
00 00 02 00 | FF FF FF FF | None. |
image/x-icon
|
A Windows Cursor signature. |
42 4D | FF FF | None. |
image/bmp
|
The string "BM ", a BMP signature.
|
47 49 46 38 37 61 | FF FF FF FF FF FF | None. |
image/gif
|
The string "GIF87a ", a GIF signature.
|
47 49 46 38 39 61 | FF FF FF FF FF FF | None. |
image/gif
|
The string "GIF89a ", a GIF signature.
|
52 49 46 46 00 00 00 00 57 45 42 50 56 50 | FF FF FF FF 00 00 00 00 FF FF FF FF FF FF | None. |
image/webp
|
The string "RIFF " followed by four
bytes followed by the string
"WEBPVP ".
|
89 50 4E 47 0D 0A 1A 0A | FF FF FF FF FF FF FF FF | None. |
image/png
|
A byte with only the highest bit set followed by the
string "PNG " followed by CR LF SUB LF, the PNG
signature.
|
FF D8 FF | FF FF FF | None. |
image/jpeg
|
The JPEG Start of Image marker followed by the indicator byte of another marker. |
To determine which audio or video type byte pattern a byte sequence matches, if any, use the following audio or video type pattern matching algorithm:
Byte Pattern | Pattern Mask | Leading Bytes to Be Ignored | Audio or Video Type | Note |
---|---|---|---|---|
1A 45 DF A3 | FF FF FF FF | None. |
video/webm
|
The WebM signature. [TODO: Use more bytes?] |
2E 73 6E 64 | FF FF FF FF | None. |
audio/basic
|
The string ".snd ", the basic audio signature.
|
46 4F 52 4D 00 00 00 00 41 49 46 46 | FF FF FF FF 00 00 00 00 FF FF FF FF | None. |
audio/aiff
|
The string "FORM " followed by four
bytes followed by the string
"AIFF ", the AIFF signature.
|
49 44 33 | FF FF FF | None. |
audio/mpeg
|
The string "ID3 ", the ID3v2-tagged MP3 signature.
|
4F 67 67 53 00 | FF FF FF FF FF | None. |
application/ogg
|
The string "OggS " followed by NUL, the Ogg container
signature.
|
4D 54 68 64 00 00 00 06 | FF FF FF FF FF FF FF FF | None. |
audio/midi
|
The string "MThd " followed by four
bytes representing the number 6 in 32 bits
(big-endian), the MIDI signature.
|
52 49 46 46 00 00 00 00 41 56 49 20 | FF FF FF FF 00 00 00 00 FF FF FF FF | None. |
video/avi
|
The string "RIFF " followed by four
bytes followed by the string
"AVI ", the AVI signature.
|
52 49 46 46 00 00 00 00 57 41 56 45 | FF FF FF FF 00 00 00 00 FF FF FF FF | None. |
audio/wave
|
The string "RIFF " followed by four
bytes followed by the string
"WAVE ", the WAVE signature.
|
MP3 audio without ID3 tags.
video/mp4
".
To determine whether a byte sequence matches the signature for MP4, use the following steps:
ftyp
"), return false.
mp4
"), return true.
This ignores the four bytes that correspond to the version number of the "major brand".
mp4
"), return true.
To determine which font type byte pattern a byte sequence matches, if any, use the following font type pattern matching algorithm:
Byte Pattern | Pattern Mask | Leading Bytes to Be Ignored | Font Type | Note |
---|---|---|---|---|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4C 50 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF | None. |
application/vnd.ms-fontobject
|
34 bytes followed by the string
"LP ", the Embedded OpenType signature.
|
00 01 00 00 | FF FF FF FF | None. | (TrueType) | 4 bytes representing the version number 1.0, a TrueType signature. |
4F 54 54 4F | FF FF FF FF | None. | (OpenType) |
The string "OTTO ", the OpenType signature.
|
74 74 63 66 | FF FF FF FF | None. | (TrueType Collection) |
The string "ttcf ", the TrueType Collection
signature.
|
77 4F 46 46 | FF FF FF FF | None. |
application/font-woff
|
The string "wOFF ", the Web Open Font Format
signature.
|
To determine which archive type byte pattern a byte sequence matches, if any, use the following archive type pattern matching algorithm:
Byte Pattern | Pattern Mask | Leading Bytes to Be Ignored | Archive Type | Note |
---|---|---|---|---|
1F 8B 08 | FF FF FF | None. |
application/x-gzip
|
The GZIP archive signature. |
50 4B 03 04 | FF FF FF FF | None. |
application/zip
|
The string "PK " followed by ETX EOT, the ZIP archive
signature.
|
52 61 72 20 1A 07 00 | FF FF FF FF FF FF FF | None. |
application/x-rar-compressed
|
The string "Rar " followed by SUB BEL NUL, the RAR
archive signature.
|
To determine the sniffed MIME type of a resource, user agents must use the following MIME type sniffing algorithm:
unknown/unknown
",
"application/unknown
", or "*/*
",
execute the rules for identifying an unknown MIME type with
the sniff-scriptable flag equal to the inverse of the
no-sniff flag and abort these steps.
text/html
", execute the
rules for distinguishing if a resource is a feed or HTML and
abort these steps.
The sniff-scriptable flag is used by the rules for identifying an unknown MIME type to determine whether to sniff for scriptable MIME types. If the setting of the sniff-scriptable flag is not specified when calling the rules for identifying an unknown MIME type, the sniff-scriptable flag must default to unset.
To determine the sniffed MIME type of a resource with an unknown MIME type, execute the following rules for identifying an unknown MIME type:
Byte Pattern | Pattern Mask | Leading Bytes to Be Ignored | Sniffed MIME Type | Note |
---|---|---|---|---|
3C 21 44 4F 43 54 59 50 45 20 48 54 4D 4C TT | FF FF DF DF DF DF DF DF DF FF DF DF DF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<!DOCTYPE HTML "
followed by a tag-terminating byte.
|
3C 48 54 4D 4C TT | FF DF DF DF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<HTML " followed by a
tag-terminating byte.
|
3C 48 45 41 44 TT | FF DF DF DF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<HEAD " followed by a
tag-terminating byte.
|
3C 53 43 52 49 50 54 TT | FF DF DF DF DF DF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<SCRIPT " followed by
a tag-terminating byte.
|
3C 49 46 52 41 4D 45 TT | FF DF DF DF DF DF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<IFRAME " followed by
a tag-terminating byte.
|
3C 48 31 TT | FF DF FF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<H1 " followed by a
tag-terminating byte.
|
3C 44 49 56 TT | FF DF DF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<DIV " followed by a
tag-terminating byte.
|
3C 46 4F 4E 54 TT | FF DF DF DF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<FONT " followed by a
tag-terminating byte.
|
3C 54 41 42 4C 45 TT | FF DF DF DF DF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<TABLE " followed by
a tag-terminating byte.
|
3C 41 TT | FF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<A " followed by a
tag-terminating byte.
|
3C 53 54 59 4C 45 TT | FF DF DF DF DF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<STYLE " followed by
a tag-terminating byte.
|
3C 54 49 54 4C 45 TT | FF DF DF DF DF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<TITLE " followed by
a tag-terminating byte.
|
3C 42 TT | FF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<B " followed by a
tag-terminating byte.
|
3C 42 4F 44 59 TT | FF DF DF DF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<BODY " followed by a
tag-terminating byte.
|
3C 42 52 TT | FF DF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<BR " followed by a
tag-terminating byte.
|
3C 50 TT | FF DF FF | Whitespace bytes. |
text/html
|
The case-insensitive string "<P " followed by a
tag-terminating byte.
|
3C 21 2D 2D TT | FF FF FF FF FF | Whitespace bytes. |
text/html
|
The string "<!-- " followed by a
tag-terminating byte.
|
3C 3F 78 6D 6C | FF FF FF FF FF | Whitespace bytes. |
text/xml
|
The string "<?xml ".
|
25 50 44 46 2D | FF FF FF FF FF | None. |
application/pdf
|
The string "%PDF- ", the PDF signature.
|
What about feeds?
Byte Pattern | Pattern Mask | Leading Bytes to Be Ignored | Sniffed MIME Type | Note |
---|---|---|---|---|
25 21 50 53 2D 41 64 6F 62 65 2D | FF FF FF FF FF FF FF FF FF FF FF | None. |
application/postscript
|
The string "%!PS-Adobe- ", the PostScript signature.
|
FE FF 00 00 | FF FF 00 00 | None. |
text/plain
|
UTF-16BE BOM |
FF FE 00 00 | FF FF 00 00 | None. |
text/plain
|
UTF-16LE BOM |
EF BB BF 00 | FF FF FF 00 | None. |
text/plain
|
UTF-8 BOM |
User agents may implicitly extend this table to support additional parsable MIME types. However, user agents should not implicitly extend this table to include additional byte patterns for any sniffed MIME type already present in this table, as doing so could introduce privilege escalation vulnerabilities. User agents must not introduce any privilege escalation vulnerabilities when extending this table.
text/plain
".
Abort these steps.
application/octet-stream
".
To determine whether a binary resource has been mislabeled as plain text, execute the following rules for distinguishing if a resource is text or binary:
text/plain
".
Abort these steps.
text/plain
".
Abort these steps.
text/plain
".
Abort these steps.
application/octet-stream
".
It is critical that the rules for distinguishing if a resource is text or binary never determine the sniffed MIME type to be a scriptable MIME type, as this could allow a privilege escalation attack.
To determine whether a feed has been mislabeled as HTML, execute the following rules for distinguishing if a resource is a feed or HTML:
<
"), increment s by 1 and exit loop
L.
!--
"), increment s by 3 and enter loop
M:
-->
"), increment s by 3 and exit
loops M and L.
!
"), increment s by 1 and enter loop
M:
>
"), increment s by 1 and exit loops
M and L.
?
"), increment s by 1 and enter loop
M:
?>
"), increment s by 2 and exit loops
M and L.
rss
"), the sniffed MIME type is
"application/rss+xml
".
Abort these steps.
feed
"), the sniffed MIME type is
"application/atom+xml
".
Abort these steps.
rdf:RDF
"), increment s
by 7 and enter loop M:
http://purl.org/rss/1.0/
"), increment
s by 24 and enter loop N:
http://www.w3.org/1999/02/22-rdf-syntax-ns#
"),
the sniffed MIME type is
"application/rss+xml
".
Abort these steps.
http://www.w3.org/1999/02/22-rdf-syntax-ns#
"),
increment s by 24 and enter loop N:
http://purl.org/rss/1.0/
"), the sniffed
MIME type is "application/rss+xml
".
Abort these steps.
It might be more efficient for the user agent to implement the rules for distinguishing if a resource is a feed or HTML in parallel with its algorithm for detecting the character encoding of an HTML document.
A context is ….
In certain contexts, it is only useful to identify resources that belong to a certain subset of MIME types. In such contexts, it is appropriate to use a context-specific sniffing algorithm in place of the MIME type sniffing algorithm in order to determine the sniffed MIME type of a resource.
A context-specific sniffing algorithm determines the sniffed MIME type of a resource only if the resource is a MIME type relevant to a particular context.
Use the MIME type sniffing algorithm.
To determine the sniffed MIME type of a resource with an image type, execute the following rules for sniffing images specifically:
To determine the sniffed MIME type of a resource with an audio or video type, execute the following rules for sniffing audio and video specifically:
To determine the sniffed MIME type of a resource fetched in a plugin context, execute the following rules for sniffing in a plugin context:
application/octet-stream
".
To determine the sniffed MIME type of a resource fetched in a style context, execute the following rules for sniffing in a style context:
To determine the sniffed MIME type of a resource fetched in a script context, execute the following rules for sniffing in a script context:
To determine the sniffed MIME type of a resource with a font type, execute the following rules for sniffing fonts specifically:
The sniffed MIME type is "text/vtt
".
The sniffed MIME type is
"text/cache-manifest
".
Special thanks to Adam Barth and Ian Hickson for maintaining previous incarnations of this document.
Thanks also to Alfred Hönes, Anne van Kesteren, Boris Zbarsky, David Singer, Henri Sivonen, Jonathan Neal, Joshua Cranmer, Larry Masinter, Mark Pilgrim, Peter Occil, Russ Cox, and Simon Pieters for their invaluable contributions.