MIME Type Lookup

Browsers decide what to do with a response from its Content-Type header, not from the extension in the URL — which is why a stylesheet served as text/plain silently does nothing. Search by extension, by MIME string, or by a whole filename to get a download header built for you.

MIME Type Lookup — File Extension to Content-Type and BackBuildFigure

The header decides, the extension does not

A MIME type — media type, in current specification language — is a type/subtype string that tells the recipient what the bytes are. A browser reads it from the Content-Type response header and uses it to choose between rendering, executing and downloading. The URL's extension has no authority at all; it matters only because most servers derive the header from it.

Where that goes wrong is specific and repeatable. A stylesheet sent as text/plain is refused in standards mode and the page renders unstyled. A JavaScript module sent as anything outside the JavaScript type list is blocked. A WebAssembly file without application/wasm fails instantiateStreaming. A PDF sent as application/octet-stream downloads instead of opening in the viewer. Each of these looks like a code problem and is a server configuration problem.

Charset, and when it matters

For anything under text/*, append ; charset=utf-8. Without it the browser falls back to a locale-dependent guess, and text that was fine on your machine arrives as mojibake somewhere else. application/json is defined as UTF-8 unconditionally, so the parameter is redundant there, though sending it harms nothing.

One parameter you must not set by hand: the boundary on multipart/form-data. If you pass a FormData object to fetch and also set Content-Type yourself, you overwrite the boundary the browser generated and the server cannot split the parts. Leave the header off entirely and let the browser write it.

Filenames that HTTP headers cannot carry

HTTP header values are effectively ASCII. A download named résumé.pdf, 報告書.xlsx or anything with an em dash cannot go into filename= directly. RFC 6266 and RFC 8187 define the fix: percent-encode the UTF-8 bytes into filename*=UTF-8''… and keep a sanitised ASCII name in plain filename= for old clients. Every current browser prefers filename* when both are present, so you send both and stop branching on User-Agent, which is what code written before about 2012 does.

Type a whole filename into the field above and the tool builds that header for you with the encoding already applied.

Never trust the type a client hands you

The type property on a browser File object comes from the operating system's extension registry, so it varies by machine and can be trivially forged — renaming shell.php to photo.jpg changes it. Server-side validation has to read the first bytes of the file and check the magic number, and it has to decide the stored type from that rather than from anything the client claimed.

Serving uploads back out is the other half. Send user content from a separate origin where possible, always send X-Content-Type-Options: nosniff so a browser cannot second-guess your header and execute the file as HTML, and treat SVG as active content, because it can contain script that runs with your origin's privileges.

Registered names, x- prefixes and collisions

IANA maintains the registry of official types. Anything with an x- prefix is a convention that was never registered, and language source types like text/x-python are all in that category. Browsers treat an unknown type the way they treat application/octet-stream, so unregistered names are safe in practice; they just are not portable guarantees.

Watch for the cases where one extension has two answers. .ts is TypeScript to a build tool and an MPEG transport stream to a video player. audio/wav and audio/x-wav both circulate for the same file. A strict allowlist that checks for one spelling will reject files sent with the other, which is why upload validation should compare against a set rather than a single string.

Questions people ask

Why does my .docx upload arrive as application/zip?

Because it is one. The modern Office formats are ZIP containers holding XML parts, so magic-byte detection sees the ZIP signature PK\x03\x04 and reports that. Robust validation opens the container and checks for the expected part names, or accepts application/zip in combination with the declared extension rather than relying on sniffing alone.

When should I deliberately send application/octet-stream?

When you do not know what the bytes are, and when you want to guarantee a download regardless of what the browser could render. Browsers will not display it inline under any circumstances. For a known type where you still want a download, keep the accurate Content-Type and add Content-Disposition: attachment instead — that way the filename and the type both survive.

Is application/javascript wrong now?

It is deprecated rather than broken. RFC 9239 made text/javascript the registered type and folded the older names into a list of aliases that servers may send and clients must accept. Every browser accepts both, so there is no urgency, but new configuration should use text/javascript.

Do I need Content-Type on a request with no body?

No. It describes the body, so on a GET, HEAD or DELETE without one it means nothing and some proxies will strip it. What you may want on those requests is Accept, which describes what you are willing to receive — a different header that is frequently confused with it.

Related