A file extension is a hint to humans. A MIME type is an instruction to machines. Confusing the two is behind a surprising share of everyday failures: uploads rejected for no visible reason, PDFs that download instead of opening, fonts that silently refuse to load, and CSS files a browser flatly declines to apply. This page covers what MIME types are, how they are decided, and the complete list you will actually reach for.
In one paragraph
A MIME type is a label like image/png in the form type/subtype. It is sent in the HTTP Content-Type header and is not stored in the file itself. Browsers trust the header over the extension. If the header is wrong, correcting the filename will not help — you must fix what the server sends.
Anatomy of a media type
Every media type has two mandatory parts and may carry optional parameters:
There are only eight top-level types, and they have not changed in decades: text, image, audio, video, font, model, application and the two composite types multipart and message. Anything that is not obviously text, media or a font ends up under application, which is why that branch is by far the largest.
The parameters matter more than people expect
The charset parameter on a text type is not decoration. If you serve UTF-8 content and declare text/html without a charset, the browser must guess the encoding, and a wrong guess is exactly how a perfectly good page ends up displaying ’ where an apostrophe should be. Always declare it.
The boundary parameter on multipart/form-data is what separates one field from the next in a file upload. It is generated per request and must not appear in any of the data being sent — which is why boundaries look like long random strings.
Prefixed subtrees
Subtypes sometimes carry a prefix that tells you their registration status:
vnd.— vendor. Owned by a specific company or product.application/vnd.ms-excel.x-— unregistered. Historically used for experimental types. Now discouraged, but survives widely:application/x-www-form-urlencodedis one of the most-used types on the web and will never be renamed.prs.— personal. Someone's private format. Rare in the wild.+xml,+json— structure suffix. Says the format is built on top of XML or JSON, so generic tooling can still parse it:image/svg+xml,application/ld+json.
Images
| Extension | MIME type | Notes |
|---|---|---|
| .jpg .jpeg | image/jpeg | Never image/jpg — that type does not exist. |
| .png | image/png | |
| .gif | image/gif | |
| .webp | image/webp | |
| .avif | image/avif | |
| .svg | image/svg+xml | Not image/svg. The suffix is required. |
| .ico | image/x-icon | image/vnd.microsoft.icon is the registered form; browsers accept both. |
| .bmp | image/bmp | |
| .tif .tiff | image/tiff | Browsers generally cannot display this. |
| .heic | image/heic | Apple photos. Little browser support. |
| .heif | image/heif | |
| .apng | image/apng | Animated PNG. |
| .jxl | image/jxl | JPEG XL. Support still limited. |
⚠️ image/jpg is not a thing
The extension is .jpg but the media type is image/jpeg, with the full four letters. Every browser will refuse or sniff around image/jpg, and strict upload validators reject it outright. The mismatch exists because early DOS filesystems allowed only three-character extensions.
Documents and office formats
| Extension | MIME type |
|---|---|
application/pdf | |
| .doc | application/msword |
| .docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
| .xls | application/vnd.ms-excel |
| .xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
| .ppt | application/vnd.ms-powerpoint |
| .pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation |
| .odt | application/vnd.oasis.opendocument.text |
| .ods | application/vnd.oasis.opendocument.spreadsheet |
| .rtf | application/rtf |
| .epub | application/epub+zip |
Those Office types are genuinely that long. They are the most-mistyped values in existence, and a single wrong character means an .xlsx download arrives as an unopenable blob. Copy them; do not retype them.
Text, data and code
| Extension | MIME type | Notes |
|---|---|---|
| .txt | text/plain | |
| .html .htm | text/html | |
| .css | text/css | Browsers refuse stylesheets served as anything else. |
| .js .mjs | text/javascript | The current standard. application/javascript is legacy. |
| .json | application/json | No charset parameter — JSON is always UTF-8. |
| .jsonld | application/ld+json | Structured data. |
| .xml | application/xml | text/xml also valid but discouraged. |
| .csv | text/csv | |
| .md | text/markdown | |
| .yaml .yml | application/yaml | Registered in 2024; text/yaml still seen. |
| .ics | text/calendar | Calendar invites. |
| .vcf | text/vcard | Contact cards. |
| .wasm | application/wasm | Required for streaming compilation. |
| — | application/x-www-form-urlencoded | Default HTML form submission. |
| — | application/octet-stream | Generic binary. "I have no idea what this is." |
Audio and video
| Extension | MIME type | Notes |
|---|---|---|
| .mp3 | audio/mpeg | Not audio/mp3. |
| .wav | audio/wav | |
| .ogg | audio/ogg | Use video/ogg if it carries video. |
| .m4a | audio/mp4 | |
| .aac | audio/aac | |
| .flac | audio/flac | |
| .opus | audio/opus | |
| .mp4 | video/mp4 | A container, not a codec. See below. |
| .webm | video/webm | |
| .mov | video/quicktime | |
| .avi | video/x-msvideo | |
| .mkv | video/x-matroska | Poor browser support. |
| .ts | video/mp2t | Streaming segments. |
| .m3u8 | application/vnd.apple.mpegurl | HLS playlist. |
💡 Why a valid video/mp4 still will not play
A media type names the container, not the codecs inside it. Two files can both be honestly labelled video/mp4 while one holds H.264 that plays everywhere and the other holds something the browser cannot decode. That is why the codecs parameter exists:
video/mp4; codecs="avc1.42E01E, mp4a.40.2"
This lets a browser decide whether it can play a file before downloading it — essential for adaptive streaming.
Fonts and archives
| Extension | MIME type | Notes |
|---|---|---|
| .woff | font/woff | |
| .woff2 | font/woff2 | The one you should be serving. |
| .ttf | font/ttf | |
| .otf | font/otf | |
| .eot | application/vnd.ms-fontobject | Legacy Internet Explorer only. |
| .zip | application/zip | |
| .gz | application/gzip | |
| .tar | application/x-tar | |
| .7z | application/x-7z-compressed | |
| .rar | application/vnd.rar | |
| .bz2 | application/x-bzip2 |
The font/ top-level type is relatively recent — fonts used to live under application/font-woff and similar. Old configuration files still carry the legacy names, and while browsers tolerate them, the modern forms are correct.
How a browser really decides the type
This is the part that resolves most confusion. The browser does not simply read the extension. It applies a priority order:
- The HTTP
Content-Typeheader. Authoritative when present and plausible. - Content sniffing. If the header is missing, generic (
application/octet-stream), or looks obviously wrong, the browser inspects the first few hundred bytes and guesses. - The file extension. Used mainly for local files opened directly from disk, where there is no HTTP header at all.
Step two is where security problems live. If a user uploads a file you serve as text/plain, and the file happens to start with <html>, a sniffing browser may decide it is really HTML and execute any script inside it — in your domain's security context. That is a stored cross-site scripting vulnerability created entirely by content sniffing.
🚨 Send nosniff on everything
X-Content-Type-Options: nosniff disables the guessing entirely and forces the browser to honour your declared type. It costs nothing, breaks nothing when your types are correct, and closes a whole category of upload-based attacks. If you serve any user-supplied file, this header is not optional.
Magic numbers: what the file really is
Most binary formats begin with a fixed signature — a "magic number" — that identifies them regardless of name or declared type. This is what a properly written upload validator checks, and why renaming files never fools it.
| Format | First bytes (hex) | As text |
|---|---|---|
| PNG | 89 50 4E 47 0D 0A 1A 0A | .PNG.... |
| JPEG | FF D8 FF | — |
| GIF | 47 49 46 38 | GIF8 |
25 50 44 46 2D | %PDF- | |
| ZIP / DOCX / XLSX | 50 4B 03 04 | PK.. |
| WebP | 52 49 46 46 … 57 45 42 50 | RIFF…WEBP |
| GZIP | 1F 8B | — |
| 7-Zip | 37 7A BC AF 27 1C | 7z… |
Notice that .docx, .xlsx and .pptx all begin with PK — the initials of Phil Katz, who created the ZIP format. Modern Office files are ZIP archives of XML, which is why a validator that only checks magic numbers sees a spreadsheet and a plain ZIP as identical. Robust validation checks the signature and looks inside.
On the command line, this is what the file utility does:
Fixing wrong Content-Type in practice
The server is sending the wrong type
Web servers map extensions to types from a configuration file. When you add a new format, the mapping may be missing, and the server falls back to application/octet-stream — which makes browsers download the file instead of rendering it.
Forcing download instead of display
Sometimes the type is right and you still want a download prompt. That is Content-Disposition, not Content-Type:
Use inline instead of attachment to display in the browser where possible. Serving a PDF as application/octet-stream to force a download works but is crude — it also breaks browsers that could have previewed it.
Data URIs
A data URI embeds a file directly in markup, and the media type is part of the URI itself:
Useful for tiny assets where an extra HTTP request costs more than the bytes. Base64 inflates data by roughly 33%, so this stops being a win quickly — a few hundred bytes is the sensible ceiling.
Need a data URI or a Base64 string?
Encode and decode Base64 right in your browser — nothing is uploaded, so it is safe for files you would not send to a server.
Open the Base64 Encoder →The rules worth remembering
- The Content-Type header wins over the file extension. Fix the header, not the name.
- Magic numbers reveal the truth. Renaming a file changes nothing about its contents.
- Always send
X-Content-Type-Options: nosniff, especially for anything users uploaded. - Declare
charset=utf-8on every text type. - It is
image/jpeg, notimage/jpg. It isaudio/mpeg, notaudio/mp3. application/octet-streammeans "unknown", and browsers respond by downloading rather than displaying.
The authoritative registry is maintained by IANA, which is worth consulting for anything obscure. But for everyday work, the tables above plus the nosniff header will handle essentially everything you meet.
Frequently Asked Questions
What is a MIME type?
A MIME type — now formally called a media type — is a two-part label like image/png or application/json that tells software what kind of data a file contains. It travels in the HTTP Content-Type header rather than being stored inside the file, which is why the same bytes can be interpreted differently depending on how they are served.
Why does my upload say wrong file type when the extension is correct?
Because most upload validators check the MIME type sent by the browser or the file's internal magic number, not the extension. Renaming photo.webp to photo.jpg changes the name but not the bytes — the file still begins with a WebP signature, so a server that inspects content correctly rejects it. Convert the file properly instead of renaming it.
What is the correct MIME type for a JavaScript file?
text/javascript. It was long considered obsolete in favour of application/javascript, but the current WHATWG specification designates text/javascript as the standard, and it is what browsers expect. For ES modules the type is the same — module status is determined by the script tag or import statement, not the MIME type.
What does X-Content-Type-Options nosniff do?
It tells the browser to trust your declared Content-Type and never guess based on file contents. Without it, a browser may sniff a file you served as text/plain, decide it looks like HTML, and execute any script inside it — the basis of a real class of attacks on user-uploaded content. Send this header on every response.
Is there a MIME type for a folder?
Not in the HTTP sense, because a folder is not transferable content. Some systems use inode/directory internally on Linux, and Google Drive uses application/vnd.google-apps.folder for its own API, but neither is a registered internet media type you would ever send in a Content-Type header.