Skip to main content

MIME Types Explained: How to Look Up the Content-Type for Any File

A practical guide to MIME types: how to find the Content-Type for a file extension, why the header decides render-vs-download, and the type/subtype format.

Published By Li Lei
#mime-type #content-type #http-headers #web-development #file-uploads

MIME Types Explained: How to Look Up the Content-Type for Any File

A MIME type is the small label that tells software what a pile of bytes actually is. It looks like text/html, image/png, or application/pdf, and it travels with files all over the web: in HTTP response headers, in upload forms, in email attachments, and in static-server config. Get it right and a browser renders your file, an upload sails through, and an attachment previews inline. Get it wrong and the same file downloads as garbage, or an API rejects it before reading a single byte.

This guide walks through what a MIME type is, the type/subtype format, why the Content-Type header carries so much weight, and how to look up the correct value for any extension. I'll work through three real examples — .json, .png, and .pdf — at the end.

What a MIME Type Actually Is

MIME stands for Multipurpose Internet Mail Extensions, which is where the term was born: it described how email could carry images and documents alongside plain text. The web borrowed the same labels, and today people call them MIME types, media types, or content types interchangeably. They all mean the same thing — a short string that identifies a format.

The key idea: the file extension on disk is only a hint. report.pdf is named .pdf, but the bytes don't carry that name once they're moving over a network. What the protocol carries is the MIME type. A web server decides, based on its own mapping table, that files ending in .pdf should be labeled application/pdf, and it stamps that label on the response. The receiving browser reads the label, not the name, to decide what to do.

That separation matters because anyone can rename a file. A .png renamed to .txt still holds PNG bytes, and the only reliable way to know the truth is to read its magic-byte signature rather than trust the name. The File MIME Type Inspector does exactly that when you need to verify what a file really is instead of what it claims to be.

The type/subtype Format

Every MIME type has two halves separated by a slash: type/subtype. The first half is a broad family, and the second pins down the exact format.

The top-level types you'll meet constantly are:

  • text/ — human-readable formats: text/html, text/css, text/csv, text/plain.
  • image/image/png, image/jpeg, image/gif, image/webp, image/svg+xml.
  • audio/ and video/audio/mpeg, video/mp4, video/webm.
  • application/ — the catch-all for structured or binary data: application/json, application/pdf, application/zip, application/wasm.

Text-based types often carry a charset parameter tacked on after a semicolon: text/html; charset=utf-8. This tells the browser how to decode the bytes into characters. Skip it on text/csv or text/html and non-ASCII content can turn into mojibake — garbled characters where your accents and Chinese columns used to be. JSON is defined as UTF-8 by spec, so the charset is implied, but for CSV and HTML you should spell it out.

When a server genuinely has no idea what a file is, it falls back to application/octet-stream. That's the universal "arbitrary binary, unknown contents" type, and a response served with it almost always triggers a save dialog instead of inline rendering.

Why the Content-Type Header Decides Everything

Here's the single most important point in this whole guide: the Content-Type response header tells the browser how to handle the body of a response. The same exact bytes will render in a tab, force a download, or break entirely depending on what that header says.

Send a PDF with Content-Type: application/pdf and most browsers open it in their built-in viewer. Send those identical bytes with Content-Type: application/octet-stream and the browser pops a save dialog. Send a stylesheet with the wrong type and the browser refuses to apply it. A JavaScript module served as text/plain instead of text/javascript won't execute. The bytes never changed — the label did.

The same logic governs uploads, just in reverse. Many APIs validate the Content-Type of an incoming request before they read any data. If your endpoint expects multipart/form-data for a file but your client sends application/json, you'll get a 415 Unsupported Media Type back instantly. This is why "my upload keeps failing" is so often a header problem and not a file problem.

The header also shows up in static-server config. In nginx you map extensions to types inside a types { } block; in Apache it's mime.types. If a freshly added extension like .webp or .wasm isn't in that table, the server falls back to application/octet-stream and browsers download your images instead of showing them. WebAssembly is especially strict: it must be served as application/wasm or streaming compilation breaks outright.

A Worked Example: .json, .png, and .pdf

Let me run through the lookup I do most often. Say I'm configuring a download endpoint and I need the exact header for three files.

  • .jsonapplication/json. No charset needed, since JSON is UTF-8 by definition. If a browser is showing your API response as a downloaded file instead of pretty-printing it, this missing or wrong header is almost always why.
  • .pngimage/png. An image type, so browsers render it inline. If yours is downloading instead, your server is probably emitting application/octet-stream because the .png mapping is absent from its config.
  • .pdfapplication/pdf. Browsers open it in their viewer by default. To force a download instead — for a generated invoice, say — keep application/pdf but add Content-Disposition: attachment rather than lying about the type.

I keep the MIME Type Lookup open in a tab while I do server config work. I type an extension like xlsx, and instead of guessing at the monstrous application/vnd.openxmlformats-officedocument.spreadsheetml.sheet string, I copy the full, correct Content-Type header in one click. It also lists the historical aliases — like application/javascript for .js, which the WHATWG standard has since replaced with text/javascript — so I can recognize the old name in legacy configs but ship the current one. Searching both directions means I can also paste a MIME type and get back every extension that maps to it.

Common Pitfalls to Avoid

A few mistakes come up again and again:

  • Sending application/javascript for .js. The modern standard mandates text/javascript. Browsers tolerate the old name, but strict servers and linters flag it.
  • Forgetting ; charset=utf-8 on text types. text/csv and text/html need it or non-ASCII bytes break. JSON doesn't, since UTF-8 is baked into its spec.
  • Trusting the extension as gospel. A renamed file still carries its original bytes. When the type really matters — security checks, upload validation — verify the magic bytes instead of believing the name.
  • Reaching for application/octet-stream as a lazy default. It forces a download and blocks inline preview. Use it deliberately when you want a save dialog, never because you couldn't be bothered to look up the real type.

Once you internalize that the MIME type — not the extension, not the file name — is what the protocol actually carries, a whole class of "why won't this render / why is my upload rejected" bugs becomes obvious. The label is the contract. Set it correctly and everything downstream behaves.


Made by Toolora · Updated 2026-06-13