MIME Sniffing

Living Standard — Last Updated 17 January 2014

This Version:
http://mimesniff.spec.whatwg.org/
Participate:
Send feedback to whatwg@whatwg.org (archives) or file a bug (open bugs)
IRC: #whatwg on Freenode
Version history:
https://github.com/whatwg/mimesniff/commits
@mimesniff
Editor:
Gordon P. Hemsley <me@gphemsley.org>
Past editors:
Adam Barth <whatwg@adambarth.com> (Google)
Ian Hickson <ian@hixie.ch> (Google)

Table of contents

  1. 1 Introduction
  2. 2 Conformance requirements
  3. 3 Terminology
  4. 4 Understanding MIME types
    1. 4.1 Parsing a MIME type
    2. 4.2 Serializing a MIME type
    3. 4.3 MIME type groups
  5. 5 Handling a resource
    1. 5.1 Interpreting the resource metadata
    2. 5.2 Reading the resource header
  6. 6 Matching a MIME type pattern
    1. 6.1 Matching an image type pattern
    2. 6.2 Matching an audio or video type pattern
      1. 6.2.1 Signature for MP4
    3. 6.3 Matching a font type pattern
    4. 6.4 Matching an archive type pattern
  7. 7 Determining the sniffed MIME type of a resource
    1. 7.1 Identifying a resource with an unknown MIME type
    2. 7.2 Sniffing a mislabeled binary resource
    3. 7.3 Sniffing a mislabeled feed
  8. 8 Context-specific sniffing
    1. 8.1 Sniffing in a browsing context
    2. 8.2 Sniffing in an image context
    3. 8.3 Sniffing in an audio or video context
    4. 8.4 Sniffing in a plugin context
    5. 8.5 Sniffing in a style context
    6. 8.6 Sniffing in a script context
    7. 8.7 Sniffing in a font context
    8. 8.8 Sniffing in a text track context
    9. 8.9 Sniffing in a cache manifest context
  9. References
  10. Acknowledgements

1 Introduction

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]

2 Conformance requirements

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.

3 Terminology

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]

4 Understanding MIME types

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.

4.1 Parsing a MIME type

To parse a MIME type, the user agent must execute the following steps:

  1. Let sequence be the byte sequence of the MIME type, where sequence[s] is byte s in sequence and sequence[0] is the first byte in sequence.
  2. If the number of bytes in sequence is less than 1, return undefined.
  3. Initialize s to 0.
  4. Initialize type and subtype to the empty string ("").
  5. Initialize parameters to the empty dictionary ({}).
  6. While sequence[s] is a whitespace character, continuously execute the following steps:
    1. Increment s by 1.
  7. Initialize t to 0.
  8. While sequence[s] is not equal to the U+002F SOLIDUS character ("/"), continuously execute the following steps:
    1. If t is greater than 127, return undefined.
    2. If sequence[s] is undefined, return undefined.
    3. Append sequence[s], converted to ASCII lowercase, to type.
    4. Increment s and t by 1.
  9. Increment s by 1.
  10. Initialize u to 0.
  11. While sequence[s] is not a whitespace character and is not equal to the U+003B SEMICOLON character (";"), continuously execute the following steps:
    1. If u is greater than 127, return undefined.
    2. If sequence[s] is undefined, return type, subtype, and parameters.
    3. Append sequence[s], converted to ASCII lowercase, to subtype.
    4. Increment s and u by 1.
  12. Enter loop L:
    1. Enter loop M:
      1. If sequence[s] is undefined or is equal to the U+003B SEMICOLON character (";"), exit loop M.
      2. While sequence[s] is a whitespace character, continuously execute the following steps:
        1. Increment s by 1.
      3. If sequence[s] is equal to the U+0022 QUOTATION MARK character ("""), execute the following steps:
        1. Increment s by 1.
        2. Enter loop N:
          1. If sequence[s] is undefined or is equal to the U+0022 QUOTATION MARK character ("""), execute the following steps:
            1. If sequence[s] is equal to the U+0022 QUOTATION MARK character ("""), increment s by 1.
            2. Exit loop N.
          2. If sequence[s] is equal to the U+005C REVERSE SOLIDUS character ("\") and sequence[s + 1] is not undefined, increment s by 1.
          3. Increment s by 1.
        Otherwise, enter loop N:
        1. If sequence[s] is undefined or is a whitespace character or is equal to the U+003B SEMICOLON character (";"), exit loop N.
        2. Increment s by 1.
    2. If sequence[s] is undefined, return type, subtype, and parameters.
    3. Increment s by 1.
    4. While sequence[s] is a whitespace character, continuously execute the following steps:
      1. Increment s by 1.
    5. Initialize name and extra to the empty string ("").
    6. Initialize p to 0.
    7. Enter loop M:
      1. Append extra to name.
      2. While sequence[s] is not a whitespace character and is not equal to the U+003D EQUALS SIGN character ("="), continuously execute the following steps:
        1. If p is greater than 127, return undefined.
        2. If sequence[s] is undefined, execute the following steps:
          1. If name is not equal to the empty string ("") and parameters[name] is undefined, set parameters[name] to null.
          2. Return type, subtype, and parameters.
        3. Append sequence[s], converted to ASCII lowercase, to name.
        4. Increment s and p by 1.
      3. While sequence[s] is a whitespace character, continuously execute the following steps:
        1. Append sequence[s] to extra.
        2. Increment s and p by 1.
      4. If sequence[s] is equal to the U+003D EQUALS SIGN character ("="), exit loop M.
    8. Increment s by 1.
    9. Initialize parameters[name] to null.
    10. While sequence[s] is a whitespace character, continuously execute the following steps:
      1. Increment s by 1.
    11. Initialize value to the empty string ("").
    12. If sequence[s] is undefined, execute the following steps:
      1. Set parameters[name] to value.
      2. Return type, subtype, and parameters.
    13. If sequence[s] is equal to the U+0022 QUOTATION MARK character ("""), execute the following steps:
      1. Increment s by 1.
      2. Enter loop M:
        1. If sequence[s] is undefined or is equal to the U+0022 QUOTATION MARK character ("""), execute the following steps:
          1. Set parameters[name] to value.
          2. If sequence[s] is equal to the U+0022 QUOTATION MARK character ("""), increment s by 1.
          3. Exit loop M.
        2. If sequence[s] is equal to the U+005C REVERSE SOLIDUS character ("\") and sequence[s + 1] is not undefined, increment s by 1.
        3. Append sequence[s] to value.
        4. Increment s by 1.
      Otherwise, enter loop M:
      1. If sequence[s] is undefined or is a whitespace character or is equal to the U+003B SEMICOLON character (";"), execute the following steps:
        1. Set parameters[name] to value.
        2. Exit loop M.
      2. Append sequence[s] to value.
      3. Increment s by 1.

The parse a MIME type algorithm is intended to be executed after any protocol-specific syntax within the MIME type has been handled.

4.2 Serializing a MIME type

To serialize a MIME type, given a type, a subtype, and a dictionary of parameters, execute the following steps:

  1. If type is undefined, is null, is equal to the empty string (""), or has a length greater than 127, return undefined.
  2. If subtype is undefined, is null, or has a length greater than 127, return undefined.
  3. Let serialization be the concatenation of type, the U+002F SOLIDUS character ("/"), and subtype.
  4. If parameters is undefined or is null, return serialization.
  5. Let names be a list of the keys in parameters, sorted ASCII case-insensitively in ascending alphabetical order.
  6. Should this special-case the "charset" or "codecs" parameters first?
  7. For each item name in names, execute the following steps:
    1. If name has a length greater than 127, return undefined.
    2. If parameters[name] is not null, execute the following steps:
      1. Append the U+003B SEMICOLON character (";") to serialization.
      2. Append name, converted to ASCII lowercase, to serialization.
      3. Append the U+003D EQUALS SIGN character ("=") to serialization.
      4. Append the U+0022 QUOTATION MARK character (""") to serialization.
      5. For each character char in parameters[name], execute the following steps:
        1. If char is equal to the U+0022 QUOTATION MARK character (""") or to the U+005C REVERSE SOLIDUS character ("\"), append the U+005C REVERSE SOLIDUS character ("\") to serialization.
        2. Append char to serialization.
      6. Append the U+0022 QUOTATION MARK character (""") to serialization.
      7. Remove name from names.
  8. For each item name in names, execute the following steps:
    1. Append the U+003B SEMICOLON character (";") to serialization.
    2. Append name, converted to ASCII lowercase, to serialization.
  9. Should this special-case the "base64" boolean parameter last?
  10. Return serialization.

4.3 MIME type groups

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:

A font type is any parsable MIME type where the MIME type portion is equal to one of the following:

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:

An archive type is any parsable MIME type where the MIME type portion is equal to one of the following:

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:

5 Handling a resource

A resource is ….

For each resource it handles, the user agent must keep track of the following associated metadata:

5.1 Interpreting the resource metadata

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:

  1. Let supplied-type be null.
  2. If the resource is retrieved via HTTP, execute the following steps:
    1. If one or more Content-Type headers are associated with the resource, execute the following steps:
      1. Set supplied-type to the value of the last 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.

      2. Set the check-for-apache-bug flag if supplied-type is exactly equal to one of the values in the following table:
        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.

    2. If one or more 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.
    [HTTP]
  3. If the resource is retrieved directly from the file system, set supplied-type to the MIME type provided by the file system.
  4. If the resource is retrieved via another protocol (such as FTP), set supplied-type to the MIME type as determined by that protocol, if any. [FTP]
  5. If supplied-type is not a parsable MIME type, the supplied MIME type is undefined. Abort these steps.
  6. The supplied MIME type is supplied-type.

5.2 Reading the resource header

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:

  1. Let buffer be a byte sequence.
  2. Read bytes of the resource into buffer until one of the following conditions is met:

    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.

  3. The resource header is buffer.

The resource header need only be determined once per resource.

6 Matching a MIME type pattern

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:

  1. Let sequence be the byte sequence to be matched, where sequence[s] is byte s in sequence and sequence[0] is the first byte in sequence.
  2. Let pattern be the byte pattern to be matched against, where pattern[p] is byte p in pattern and pattern[0] is the first byte in pattern.
  3. Let mask be the pattern mask, where mask[m] is byte m in mask and mask[0] is the first byte in mask.
  4. Let length be the number of bytes in pattern, which is equal to the number of bytes in mask.
  5. If the number of bytes in sequence is less than length, return false.
  6. Initialize s, p, and m to 0.
  7. Enter loop L:
    1. If sequence[s] is undefined, return false.
    2. If sequence[s] is not one of the leading bytes to be ignored, exit loop L.
    3. Increment s by 1.
  8. Enter loop L:
    1. If pattern[p] is undefined, exit loop L.
    2. Let masked-data be the result of applying the bitwise AND operator to sequence[s] and mask[m].
    3. If masked-data is not equal to pattern[p], return false.
    4. Increment s, p, and m by 1.
  9. Return true.

6.1 Matching an image type pattern

To determine which image type byte pattern a byte sequence matches, if any, use the following image type pattern matching algorithm:

  1. Execute the following steps for each row in the following table:
    1. Let pattern-matched be the result of executing the pattern matching algorithm with the following parameters:
      • Let the byte sequence to be matched be the byte sequence to be matched.
      • Let the byte pattern to be matched against be the value in the first column of the current row.
      • Let the pattern mask be the value in the second column of the current row.
      • Let the leading bytes to be ignored be the value in the third column of the current row.
    2. If pattern-matched is true, return the value in the fourth column of the current row.
    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.
  2. Return undefined.

6.2 Matching an audio or video type pattern

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:

  1. Execute the following steps for each row in the following table:
    1. Let pattern-matched be the result of executing the pattern matching algorithm with the following parameters:
      • Let the byte sequence to be matched be the byte sequence to be matched.
      • Let the byte pattern to be matched against be the value in the first column of the current row.
      • Let the pattern mask be the value in the second column of the current row.
      • Let the leading bytes to be ignored be the value in the third column of the current row.
    2. If pattern-matched is true, return the value in the fourth column of the current row.
    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.

  2. If the byte sequence to be matched matches the signature for MP4, return "video/mp4".
  3. Return undefined.

6.2.1 Signature for MP4

To determine whether a byte sequence matches the signature for MP4, use the following steps:

  1. Let sequence be the byte sequence to be matched, where sequence[s] is byte s in sequence and sequence[0] is the first byte in sequence.
  2. Let length be the number of bytes in sequence.
  3. If length is less than 12, return false.
  4. Let box-size be the four bytes from sequence[0] to sequence[3], interpreted as a 32-bit unsigned big-endian integer.
  5. If length is less than box-size or if box-size modulo 4 is not equal to 0, return false.
  6. If the four bytes from sequence[4] to sequence[7] are not equal to 0x66 0x74 0x79 0x70 ("ftyp"), return false.
  7. If the three bytes from sequence[8] to sequence[10] are equal to 0x6D 0x70 0x34 ("mp4"), return true.
  8. Let bytes-read be 16.

    This ignores the four bytes that correspond to the version number of the "major brand".

  9. While bytes-read is less than box-size, continuously loop through these steps:
    1. If the three bytes from sequence[bytes-read] to sequence[bytes-read + 2] are equal to 0x6D 0x70 0x34 ("mp4"), return true.
    2. Increment bytes-read by 4.
  10. Return false.

6.3 Matching a font type pattern

To determine which font type byte pattern a byte sequence matches, if any, use the following font type pattern matching algorithm:

  1. Execute the following steps for each row in the following table:
    1. Let pattern-matched be the result of executing the pattern matching algorithm with the following parameters:
      • Let the byte sequence to be matched be the byte sequence to be matched.
      • Let the byte pattern to be matched against be the value in the first column of the current row.
      • Let the pattern mask be the value in the second column of the current row.
      • Let the leading bytes to be ignored be the value in the third column of the current row.
    2. If pattern-matched is true, return the value in the fourth column of the current row.
    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.
  2. Return undefined.

6.4 Matching an archive type pattern

To determine which archive type byte pattern a byte sequence matches, if any, use the following archive type pattern matching algorithm:

  1. Execute the following steps for each row in the following table:
    1. Let pattern-matched be the result of executing the pattern matching algorithm with the following parameters:
      • Let the byte sequence to be matched be the byte sequence to be matched.
      • Let the byte pattern to be matched against be the value in the first column of the current row.
      • Let the pattern mask be the value in the second column of the current row.
      • Let the leading bytes to be ignored be the value in the third column of the current row.
    2. If pattern-matched is true, return the value in the fourth column of the current row.
    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.
  2. Return undefined.

7 Determining the sniffed MIME type of a resource

To determine the sniffed MIME type of a resource, user agents must use the following MIME type sniffing algorithm:

  1. If the supplied MIME type is undefined or if the MIME type portion of the supplied MIME type is equal to "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.
  2. If the no-sniff flag is set, the sniffed MIME type is the supplied MIME type. Abort these steps.
  3. If the check-for-apache-bug flag is set, execute the rules for distinguishing if a resource is text or binary and abort these steps.
  4. If the supplied MIME type is an XML type, the sniffed MIME type is the supplied MIME type. Abort these steps.
  5. If the MIME type portion of the supplied MIME type is equal to "text/html", execute the rules for distinguishing if a resource is a feed or HTML and abort these steps.
  6. If the supplied MIME type is an image type supported by the user agent, let matched-type be the result of executing the image type pattern matching algorithm with the resource header as the byte sequence to be matched.
  7. If matched-type is not undefined, the sniffed MIME type is matched-type. Abort these steps.
  8. If the supplied MIME type is an audio or video type supported by the user agent, let matched-type be the result of executing the audio or video type pattern matching algorithm with the resource header as the byte sequence to be matched.
  9. If matched-type is not undefined, the sniffed MIME type is matched-type. Abort these steps.
  10. The sniffed MIME type is the supplied MIME type.

7.1 Identifying a resource with an unknown MIME type

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:

  1. If the sniff-scriptable flag is set, execute the following steps for each row in the following table:
    1. Let pattern-matched be the result of executing the pattern matching algorithm with the following parameters:
      • Let the byte sequence to be matched be the resource header.
      • Let the byte pattern to be matched against be the value in the first column of the current row.
      • Let the pattern mask be the value in the second column of the current row.
      • Let the leading bytes to be ignored be the value in the third column of the current row.
    2. If pattern-matched is true, the sniffed MIME type is the value in the fourth column of the current row. Abort these steps.
    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?

  2. Execute the following steps for each row in the following table:
    1. Let pattern-matched be the result of executing the pattern matching algorithm with the following parameters:
      • Let the byte sequence to be matched be the resource header.
      • Let the byte pattern to be matched against be the value in the first column of the current row.
      • Let the pattern mask be the value in the second column of the current row.
      • Let the leading bytes to be ignored be the value in the third column of the current row.
    2. If pattern-matched is true, the sniffed MIME type is the value in the fourth column of the current row. Abort these steps.
    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.

  3. Let matched-type be the result of executing the image type pattern matching algorithm with the resource header as the byte sequence to be matched.
  4. If matched-type is not undefined, the sniffed MIME type is matched-type. Abort these steps.
  5. Let matched-type be the result of executing the audio or video type pattern matching algorithm with the resource header as the byte sequence to be matched.
  6. If matched-type is not undefined, the sniffed MIME type is matched-type. Abort these steps.
  7. Let matched-type be the result of executing the archive type pattern matching algorithm with the resource header as the byte sequence to be matched.
  8. If matched-type is not undefined, the sniffed MIME type is matched-type. Abort these steps.
  9. If the resource header contains no binary data bytes, the sniffed MIME type is "text/plain". Abort these steps.
  10. The sniffed MIME type is "application/octet-stream".

7.2 Sniffing a mislabeled binary resource

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:

  1. Let length be the number of bytes in the resource header.
  2. If length is greater than or equal to 2 and the first 2 bytes of the resource header are equal to 0xFE 0xFF (UTF-16BE BOM) or 0xFF 0xFE (UTF-16LE BOM), the sniffed MIME type is "text/plain". Abort these steps.
  3. If length is greater than or equal to 3 and the first 3 bytes of the resource header are equal to 0xEF 0xBB 0xBF (UTF-8 BOM), the sniffed MIME type is "text/plain". Abort these steps.
  4. If the resource header contains no binary data bytes, the sniffed MIME type is "text/plain". Abort these steps.
  5. The sniffed MIME type is "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.

7.3 Sniffing a mislabeled feed

To determine whether a feed has been mislabeled as HTML, execute the following rules for distinguishing if a resource is a feed or HTML:

  1. Let sequence be the resource header, where sequence[s] is byte s in sequence and sequence[0] is the first byte in sequence.
  2. Let length be the number of bytes in sequence.
  3. Initialize s to 0.
  4. If length is greater than or equal to 3 and the three bytes from sequence[0] to sequence[2] are equal to 0xEF 0xBB 0xBF (UTF-8 BOM), increment s by 3.
  5. While s is less than length, continuously loop through these steps:
    1. Enter loop L:
      1. If sequence[s] is undefined, the sniffed MIME type is the supplied MIME type. Abort these steps.
      2. If sequence[s] is equal to 0x3C ("<"), increment s by 1 and exit loop L.
      3. If sequence[s] is not a whitespace byte, the sniffed MIME type is the supplied MIME type. Abort these steps.
      4. Increment s by 1.
    2. Enter loop L:
      1. If sequence[s] is undefined, the sniffed MIME type is the supplied MIME type. Abort these steps.
      2. If length is greater than or equal to s + 3 and the three bytes from sequence[s] to sequence[s + 2] are equal to 0x21 0x2D 0x2D ("!--"), increment s by 3 and enter loop M:
        1. If sequence[s] is undefined, the sniffed MIME type is the supplied MIME type. Abort these steps.
        2. If length is greater than or equal to s + 3 and the three bytes from sequence[s] to sequence[s + 2] are equal to 0x2D 0x2D 0x3E ("-->"), increment s by 3 and exit loops M and L.
        3. Increment s by 1.
      3. If length is greater than or equal to s + 1 and sequence[s] is equal to 0x21 ("!"), increment s by 1 and enter loop M:
        1. If sequence[s] is undefined, the sniffed MIME type is the supplied MIME type. Abort these steps.
        2. If length is greater than or equal to s + 1 and sequence[s] is equal to 0x3E (">"), increment s by 1 and exit loops M and L.
        3. Increment s by 1.
      4. If length is greater than or equal to s + 1 and sequence[s] is equal to 0x3F ("?"), increment s by 1 and enter loop M:
        1. If sequence[s] is undefined, the sniffed MIME type is the supplied MIME type. Abort these steps.
        2. If length is greater than or equal to s + 2 and the two bytes from sequence[s] to sequence[s + 1] are equal to 0x3F 0x3E ("?>"), increment s by 2 and exit loops M and L.
        3. Increment s by 1.
      5. If length is greater than or equal to s + 3 and the three bytes from sequence[s] to sequence[s + 2] are equal to 0x72 0x73 0x73 ("rss"), the sniffed MIME type is "application/rss+xml". Abort these steps.
      6. If length is greater than or equal to s + 4 and the four bytes from sequence[s] to sequence[s + 3] are equal to 0x66 0x65 0x65 0x64 ("feed"), the sniffed MIME type is "application/atom+xml". Abort these steps.
      7. If length is greater than or equal to s + 7 and the seven bytes from sequence[s] to sequence[s + 6] are equal to 0x72 0x64 0x66 0x3A 0x52 0x44 0x46 ("rdf:RDF"), increment s by 7 and enter loop M:
        1. If sequence[s] is undefined, the sniffed MIME type is the supplied MIME type. Abort these steps.
        2. If length is greater than or equal to s + 24 and the twenty-four bytes from sequence[s] to sequence[s + 23] are equal to 0x68 0x74 0x74 0x70 0x3A 0x2F 0x2F 0x70 0x75 0x72 0x6C 0x2E 0x6F 0x72 0x67 0x2F 0x72 0x73 0x73 0x2F 0x31 0x2E 0x30 0x2F ("http://purl.org/rss/1.0/"), increment s by 24 and enter loop N:
          1. If sequence[s] is undefined, the sniffed MIME type is the supplied MIME type. Abort these steps.
          2. If length is greater than or equal to s + 43 and the forty-three bytes from sequence[s] to sequence[s + 42] are equal to 0x68 0x74 0x74 0x70 0x3A 0x2F 0x2F 0x77 0x77 0x77 0x2E 0x77 0x33 0x2E 0x6F 0x72 0x67 0x2F 0x31 0x39 0x39 0x39 0x2F 0x30 0x32 0x2F 0x32 0x32 0x2D 0x72 0x64 0x66 0x2D 0x73 0x79 0x6E 0x74 0x61 0x78 0x2D 0x6E 0x73 0x23 ("http://www.w3.org/1999/02/22-rdf-syntax-ns#"), the sniffed MIME type is "application/rss+xml". Abort these steps.
          3. Increment s by 1.
        3. If length is greater than or equal to s + 24 and the twenty-four bytes from sequence[s] to sequence[s + 23] are equal to 0x68 0x74 0x74 0x70 0x3A 0x2F 0x2F 0x77 0x77 0x77 0x2E 0x77 0x33 0x2E 0x6F 0x72 0x67 0x2F 0x31 0x39 0x39 0x39 0x2F 0x30 0x32 0x2F 0x32 0x32 0x2D 0x72 0x64 0x66 0x2D 0x73 0x79 0x6E 0x74 0x61 0x78 0x2D 0x6E 0x73 0x23 ("http://www.w3.org/1999/02/22-rdf-syntax-ns#"), increment s by 24 and enter loop N:
          1. If sequence[s] is undefined, the sniffed MIME type is the supplied MIME type. Abort these steps.
          2. If length is greater than or equal to s + 43 and the forty-three bytes from sequence[s] to sequence[s + 42] are equal to 0x68 0x74 0x74 0x70 0x3A 0x2F 0x2F 0x70 0x75 0x72 0x6C 0x2E 0x6F 0x72 0x67 0x2F 0x72 0x73 0x73 0x2F 0x31 0x2E 0x30 0x2F ("http://purl.org/rss/1.0/"), the sniffed MIME type is "application/rss+xml". Abort these steps.
          3. Increment s by 1.
        4. Increment s by 1.
      8. The sniffed MIME type is the supplied MIME type. Abort these steps.
  6. The sniffed MIME type is the supplied MIME type.

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.

8 Context-specific sniffing

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.

8.1 Sniffing in a browsing context

Use the MIME type sniffing algorithm.

8.2 Sniffing in an image context

To determine the sniffed MIME type of a resource with an image type, execute the following rules for sniffing images specifically:

  1. If the supplied MIME type is an XML type, the sniffed MIME type is the supplied MIME type. Abort these steps.
  2. Let image-type-matched be the result of executing the image type pattern matching algorithm with the resource header as the byte sequence to be matched.
  3. If image-type-matched is not undefined, the sniffed MIME type is image-type-matched. Abort these steps.
  4. The sniffed MIME type is the supplied MIME type.

8.3 Sniffing in an audio or video context

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:

  1. If the supplied MIME type is an XML type, the sniffed MIME type is the supplied MIME type. Abort these steps.
  2. Let audio-or-video-type-matched be the result of executing the audio or video type pattern matching algorithm with the resource header as the byte sequence to be matched.
  3. If audio-or-video-type-matched is not undefined, the sniffed MIME type is audio-or-video-type-matched. Abort these steps.
  4. The sniffed MIME type is the supplied MIME type.

8.4 Sniffing in a plugin context

To determine the sniffed MIME type of a resource fetched in a plugin context, execute the following rules for sniffing in a plugin context:

  1. If the supplied MIME type is undefined, the sniffed MIME type is "application/octet-stream".
  2. The sniffed MIME type is the supplied MIME type.

8.5 Sniffing in a style context

To determine the sniffed MIME type of a resource fetched in a style context, execute the following rules for sniffing in a style context:

  1. If the supplied MIME type is undefined, ….
  2. The sniffed MIME type is the supplied MIME type.

8.6 Sniffing in a script 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:

  1. If the supplied MIME type is undefined, ….
  2. The sniffed MIME type is the supplied MIME type.

8.7 Sniffing in a font context

To determine the sniffed MIME type of a resource with a font type, execute the following rules for sniffing fonts specifically:

  1. If the supplied MIME type is an XML type, the sniffed MIME type is the supplied MIME type. Abort these steps.
  2. Let font-type-matched be the result of executing the font type pattern matching algorithm with the resource header as the byte sequence to be matched.
  3. If font-type-matched is not undefined, the sniffed MIME type is font-type-matched. Abort these steps.
  4. The sniffed MIME type is the supplied MIME type.

8.8 Sniffing in a text track context

The sniffed MIME type is "text/vtt".

8.9 Sniffing in a cache manifest context

The sniffed MIME type is "text/cache-manifest".

References

[ASCII]
(Non-normative) ASCII format for Network Interchange, Vint Cerf. IETF.
[ENCODING]
Encoding, Anne van Kesteren. WHATWG.
[FTP]
(Non-normative) File Transfer Protocol, J. Postel and J. Reynolds. IETF.
[HTML]
HTML, Ian Hickson. WHATWG.
[HTTP]
Hypertext Transfer Protocol -- HTTP/1.1, Roy Fielding, James Gettys, Jeffrey Mogul et al.. IETF.
[MIMETYPE]
(Non-normative) Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types, Ned Freed and Nathaniel Borenstein. IETF.
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels, Scott Bradner. IETF.
[RFC3023]
(Non-normative) XML Media Types, M. Murata, S. St. Laurent and D. Kohn. IETF.
[SECCONTSNIFF]
(Non-normative) Secure Content Sniffing for Web Browsers, or How to Stop Papers from Reviewing Themselves, Adam Barth, Juan Caballero and Dawn Song.

Acknowledgements

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.