Back to HTTP Headers

Content-Type content both

Tells the recipient what media type the message body is, and how it's encoded — so it knows how to parse it.

What it does

Content-Type tells the recipient the media type of the message body and, optionally, the character encoding. Without it, the recipient has to guess — and guessing wrong means broken JSON parsers, garbled text, or corrupted file downloads.

It works in both directions. On a response, the server tells the client what it's sending back. On a request, the client tells the server what format the request body is in (required for POST/PUT/PATCH with a body).

Syntax

Content-Type: <media-type>[; charset=<charset>][; boundary=<boundary>]

Examples:

Content-Type: text/html; charset=UTF-8
Content-Type: application/json
Content-Type: application/x-www-form-urlencoded
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Type: image/png
Content-Type: application/pdf

The media type is a two-part identifier: type/subtype. The charset parameter is most relevant for text types. The boundary parameter is required for multipart/* types — it's the separator string the parser uses to split parts.

Common media types

Media type Used for
application/json JSON API responses and request bodies
text/html; charset=UTF-8 HTML pages
application/x-www-form-urlencoded HTML form submissions (default)
multipart/form-data File uploads and forms with binary data
text/plain Plain text
application/xml XML data
application/octet-stream Generic binary / unknown file type
image/jpeg, image/png, image/webp Images
application/pdf PDF files
text/csv CSV data

How it interacts with other headers

Content-Type and Accept are a matched pair. The client sends Accept to say what it can handle; the server sends Content-Type to say what it actually returned. If the server sends a type not in the client's Accept list, a strict client should return a 406 Not Acceptable — though many clients ignore this in practice.

Content-Encoding describes a transformation applied on top of whatever Content-Type says. So Content-Type: text/html with Content-Encoding: gzip means "the body is gzip-compressed HTML." The client decompresses first, then parses as HTML.

Content-Disposition can pair with Content-Type to trigger a file download — the Content-Type tells the browser what it is, Content-Disposition: attachment tells it not to display it inline.

Common mistakes and gotchas

Sending JSON without setting Content-Type. The single most common API mistake. If you POST a JSON body without Content-Type: application/json, many server frameworks (Laravel, Express, Django, etc.) won't automatically parse it as JSON. The body arrives as a raw string and your JSON parser never runs.

Including a charset on binary types. Content-Type: image/jpeg; charset=UTF-8 is meaningless — charset only applies to text types. It won't break things, but it signals confusion about what the header actually does.

Getting the boundary wrong on multipart. The boundary in Content-Type: multipart/form-data; boundary=XYZ must exactly match the separator strings in the body. Mismatch = the body doesn't parse. Browsers handle this automatically when you let a <form> set the Content-Type — problems arise when you construct multipart bodies manually.

Forgetting Content-Type on file download responses. Sending a file with Content-Type: application/octet-stream works, but the browser has no idea what application to suggest for opening it. Set the specific type (application/pdf, image/png) so the OS knows what to do with the download.

Content-Type sniffing. Old browsers (especially IE) would ignore a declared Content-Type and sniff the content to guess the real type. This is a security risk — a server declaring text/plain for user-uploaded content might have the browser execute it as HTML/JS. The X-Content-Type-Options: nosniff header disables this.

Real-world examples

A JSON API response:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 47

{"id": 1, "name": "Sainesh", "role": "admin"}

A form submission (browser default):

POST /login HTTP/1.1
Content-Type: application/x-www-form-urlencoded

email=sainesh%40example.com&password=hunter2

A file upload:

POST /upload HTTP/1.1
Content-Type: multipart/form-data; boundary=----Boundary123

------Boundary123
Content-Disposition: form-data; name="file"; filename="photo.jpg"
Content-Type: image/jpeg

<binary data>
------Boundary123--

Forcing a file download:

HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Disposition: attachment; filename="invoice-2026.pdf"

FAQ

Does Content-Type on a GET request make sense?

Rarely. GET requests shouldn't have a body, so Content-Type in a GET request is meaningless and usually ignored. Some non-standard APIs accept GET requests with bodies (bad practice), in which case you'd set it. But in standard HTTP, Content-Type on a request only matters for methods that send a body: POST, PUT, PATCH.

What's the difference between application/x-www-form-urlencoded and multipart/form-data?

application/x-www-form-urlencoded encodes form values as key=value pairs separated by &, with special characters percent-encoded. It's compact and works well for simple text fields. multipart/form-data splits the body into parts separated by a boundary string, which lets each part carry its own headers — essential for binary file data where percent-encoding would be huge and slow.

Should I always include charset=UTF-8 for JSON?

The JSON spec (RFC 8259) states that JSON must be encoded as UTF-8, UTF-16, or UTF-32, with UTF-8 being the default and overwhelmingly the standard. So application/json implies UTF-8. Adding ; charset=UTF-8 is redundant but harmless. Some older clients appreciate the explicitness. RFC 7159 and later RFC 8259 actually deprecated the use of a charset parameter for JSON entirely.

What should I use for a generic binary file download?

application/octet-stream is the catch-all for binary data when you don't know or can't express the specific type. Browsers will offer it as a download rather than trying to display it. If you do know the type (PDF, ZIP, PNG), use the specific MIME type — it gives the browser and OS better information for handling the file after download.

Can the same response have multiple Content-Types?

No, Content-Type is a single-value header. If you need to return multiple types (e.g., different parts of a response with different types), that's what multipart/* types are for — each part carries its own Content-Type.

Fun fact

The term "MIME type" in Content-Type comes from MIME — Multipurpose Internet Mail Extensions — originally defined in 1992 for email attachments. When the web needed a way to describe different content types in HTTP, it borrowed MIME wholesale. So every time you set Content-Type: application/json, you're using a standard invented for email that predates the web browser.