A MIME type, also called a media type or Content-Type, is a type/subtype label that tells a client what the bytes in an HTTP body, upload or email part are. This sheet is for developers who set response headers, configure a static file server or validate uploads. The confusion it clears up: only some names you see in the wild are registered with IANA, and text/javascript is now the correct one for scripts.
| Part | Example | Rule |
|---|---|---|
| Type | `text` | One of the registered top-level types |
| Subtype | `html` | Up to 127 characters by the grammar, 64 recommended |
| Suffix | `+json` in `application/ld+json` | Says the structure follows that syntax |
| Parameter | `charset=utf-8` | Written after a semicolon, name is case-insensitive |
| Facet | `vnd.` in `application/vnd.api+json` | Vendor tree, `prs.` is personal, `x.` is unregistered |
Type and subtype are case-insensitive, so Application/JSON equals application/json. The registry lists these top-level types: application, audio, font, haptics, image, message, model, multipart, text and video, plus example, which is reserved for documentation.
| Extension | MIME type | Defined by |
|---|---|---|
| `.html` | `text/html` | W3C HTML |
| `.css` | `text/css` | W3C CSS |
| `.js`, `.mjs` | `text/javascript` | RFC 9239 |
| `.json` | `application/json` | RFC 8259 |
| `.jsonld` | `application/ld+json` | W3C JSON-LD |
| `.webmanifest` | `application/manifest+json` | W3C Web App Manifest |
| `.xml` | `application/xml` or `text/xml` | RFC 7303 |
| `.csv` | `text/csv` | RFC 4180, RFC 7111 |
| `.md` | `text/markdown` | RFC 7763 |
| `.txt` | `text/plain` | RFC 2046 |
| `.ics` | `text/calendar` | RFC 5545 |
| `.yaml`, `.yml` | `application/yaml` | RFC 9512 |
| Extension | MIME type | Defined by |
|---|---|---|
| `.png` | `image/png` | W3C PNG |
| `.jpg`, `.jpeg` | `image/jpeg` | RFC 2045, RFC 2046 |
| `.gif` | `image/gif` | RFC 2045, RFC 2046 |
| `.webp` | `image/webp` | RFC 9649 |
| `.avif` | `image/avif` | Alliance for Open Media |
| `.apng` | `image/apng` | W3C PNG group |
| `.svg` | `image/svg+xml` | W3C SVG |
| `.ico` | `image/vnd.microsoft.icon` | IANA registration |
| `.bmp` | `image/bmp` | RFC 7903 |
| `.tif`, `.tiff` | `image/tiff` | RFC 3302 |
| `.heic` | `image/heic` | ISO/IEC |
| Extension | MIME type | Defined by |
|---|---|---|
| `.woff` | `font/woff` | RFC 8081 |
| `.woff2` | `font/woff2` | RFC 8081 |
| `.ttf` | `font/ttf` | RFC 8081 |
| `.otf` | `font/otf` | RFC 8081 |
| `.mp3` | `audio/mpeg` | RFC 3003 |
| `.ogg` | `audio/ogg` | RFC 5334, RFC 7845 |
| `.flac` | `audio/flac` | RFC 9639 |
| `.m4a` | `audio/mp4` | RFC 4337 |
| `.mp4` | `video/mp4` | RFC 4337, RFC 6381 |
| `.mpeg` | `video/mpeg` | RFC 2045, RFC 2046 |
| `.mov` | `video/quicktime` | RFC 6381 |
| Extension | MIME type | Defined by |
|---|---|---|
| `.pdf` | `application/pdf` | RFC 8118 |
| `.zip` | `application/zip` | IANA registration |
| `.gz` | `application/gzip` | RFC 6713 |
| `.rar` | `application/vnd.rar` | IANA registration |
| `.wasm` | `application/wasm` | W3C WebAssembly |
| `.epub` | `application/epub+zip` | W3C EPUB |
| `.xlsx` | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` | IANA registration |
| `.docx` | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` | IANA registration |
| unknown binary | `application/octet-stream` | RFC 2045, RFC 2046 |
| Seen in the wild | Use instead |
|---|---|
| `application/javascript`, `text/ecmascript` | `text/javascript` |
| `image/x-icon` | `image/vnd.microsoft.icon` |
| `application/x-gzip` | `application/gzip` |
| `application/x-tar`, `video/webm`, `audio/wav` | Not in the IANA registry when checked, but servers and browsers use them |
RFC 6838 says new types should not use an x- prefix, and widely deployed x- names can be registered under their current spelling.
| Content-Type | When |
|---|---|
| `application/json` | JSON API bodies |
| `application/x-www-form-urlencoded` | Default HTML form encoding |
| `multipart/form-data; boundary=...` | File uploads, RFC 7578 |
| `application/problem+json` | Error bodies, RFC 9457 |
| `application/octet-stream` | Raw bytes, forced downloads |
const http = require('http');
http.createServer((q, s) => {
s.setHeader('Content-Type', 'application/json; charset=utf-8');
s.setHeader('X-Content-Type-Options', 'nosniff');
s.end('{"ok":true}');
}).listen(8765);
Checked with curl, the response carried both headers:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
X-Content-Type-Options: nosniff
Always pair an explicit type with nosniff so browsers do not guess.
const path = require('path');
const types = {'.html':'text/html; charset=utf-8','.js':'text/javascript; charset=utf-8','.json':'application/json','.svg':'image/svg+xml','.woff2':'font/woff2','.wasm':'application/wasm'};
for (const f of ['a.html','b.js','c.json','d.svg','e.woff2','f.wasm','g.xyz'])
console.log(f, '->', types[path.extname(f)] ?? 'application/octet-stream');
a.html -> text/html; charset=utf-8
b.js -> text/javascript; charset=utf-8
c.json -> application/json
d.svg -> image/svg+xml
e.woff2 -> font/woff2
f.wasm -> application/wasm
g.xyz -> application/octet-stream
Falling back to application/octet-stream is what RFC 9110 suggests when the type is unknown.
import mimetypes
for f in ['a.json','a.mjs','a.svg','a.tar.gz','a.unknown']:
print(f, mimetypes.guess_type(f))
a.json ('application/json', None)
a.mjs ('text/javascript', None)
a.svg ('image/svg+xml', None)
a.tar.gz ('application/x-tar', 'gzip')
a.unknown (None, None)
The second tuple item is the content encoding, so .tar.gz comes back as tar plus gzip.
http {
include /etc/nginx/mime.types;
types { text/javascript js mjs; }
default_type application/octet-stream;
}
On nginx 1.24 with the stock Ubuntu mime.types, .js came back as application/javascript and .mjs as application/octet-stream. With the types line above, both returned text/javascript. Nginx prints a "duplicate extension" warning when you override one, which is harmless.
console.log(new Blob(['{}'], {type: 'Application/JSON; Charset=UTF-8'}).type);
console.log(new Response('hi').headers.get('content-type'));
console.log(new Response(new URLSearchParams({a: '1'})).headers.get('content-type'));
console.log(new Response(JSON.stringify({a: 1})).headers.get('content-type'));
application/json; charset=utf-8
text/plain;charset=UTF-8
application/x-www-form-urlencoded;charset=UTF-8
text/plain;charset=UTF-8
A JSON string body is sent as text/plain, so set Content-Type: application/json yourself. Blob lowercases the type.
import email.message
m = email.message.Message()
m['Content-Type'] = 'Text/HTML; charset=UTF-8; q=1'
print(m.get_content_type(), m.get_content_maintype(), m.get_content_subtype(), m.get_param('charset'))
text/html text html UTF-8
Do not split on / and ; by hand when a parameter value can be quoted, such as boundary="abc123".
application/octet-stream: with nosniff set, the Fetch standard blocks a script-like request when the type is missing or not a JavaScript MIME type, and a stylesheet unless the type is text/css. Browsers then refuse module scripts with a MIME error. Map .mjs and .js to text/javascript.application/javascript in new code: RFC 9239 (2022) marks it obsolete and makes text/javascript the only common name. Browsers still accept the old one, but linters and new servers should not emit it.text/plain;charset=UTF-8, so frameworks that switch on Content-Type ignore it. Add headers: {'Content-Type': 'application/json'}.Content-Type: multipart/form-data by hand: the boundary parameter is missing, and the server cannot split the parts. Let FormData set the header.text/html or image/svg+xml: both can run script in your origin. Store the type from your own allowlist, serve uploads from a separate domain, and use Content-Disposition: attachment where possible.file.type: it comes from the file extension, not the content. Check magic bytes on the server, for example with file --mime-type, which printed application/json for a JSON file and application/octet-stream for a bare PNG signature.charset default: RFC 6657 replaced the fixed US-ASCII default for text/*, so each subtype defines its own default or has none. For HTML and CSS, send charset=utf-8 or declare it in the document. JSON needs no charset parameter.x- name as if it were registered: application/x-tar, audio/wav and video/webm work everywhere but are not in the IANA registry as of this check. Do not invent new x- names.data: URIs where the MIME type sits before ;base64,.ttf and .woff2 fonts, which need the font/ttf or font/woff2 typetext/csv and TSV files in a sortable table