HTTP 415 vs 406 — Unsupported Media Type vs Not Acceptable Comparison
http 415 vs 406: 415 is about Content-Type the client sent, 406 is about Accept the client wants. Learn the request vs response negotiation difference.
Quick Cheat Sheet
| Aspect | 415 Unsupported Media Type | 406 Not Acceptable |
|---|---|---|
| About which header? | Content-Type (request) |
Accept (request) |
| Direction | Request body sent by client | Response body the client wants |
| When? | Server can't parse the body | Server can't produce an acceptable representation |
The Direction of Negotiation
This pair confuses developers because both relate to media types — but they sit on opposite sides of the request/response cycle.
415 Unsupported Media Type (RFC 9110 § 15.5.16): the client sent a request body (typically POST/PUT/PATCH) with a Content-Type the server doesn't understand. Example: posting text/csv to an endpoint that only accepts application/json.
406 Not Acceptable (RFC 9110 § 15.5.7): the client sent an Accept header listing only formats the server can't produce. Example: Accept: application/xml on a JSON-only API.
A Mnemonic
- 415 = "Unsupported what you sent me"
- 406 = "Not Acceptable what you want back"
Real-World Frequency
415 is common. Any modern API will return it if you forget Content-Type: application/json on a JSON POST, or send multipart/form-data to a JSON endpoint. Frameworks like Spring, Express+body-parser, FastAPI, and Rails all check this.
406 is rare. Most APIs serve only one or two formats and ignore Accept, returning 200 with their default representation regardless. This is technically against RFC, but in practice clients tolerate it. You'll see real 406 mostly in older XML-vs-JSON APIs and some Rails apps that explicitly call respond_to.
What About Charset?
Content-Type: application/json; charset=utf-16 should arguably return 415 if the server only handles UTF-8, though most parsers just attempt UTF-8 and fail with 400.
Best Practice
For 415, always include an Accept-Patch (for PATCH) or document the allowed Content-Types in the response body.
For 406, you can either return 406 strictly, or return your default representation with Vary: Accept and let the client deal with it. The pragmatic choice depends on your API consumers.
Adjacent Codes
- 400 Bad Request is sometimes returned instead of 415 when the framework can't tell the difference between "wrong type" and "broken JSON."
- 422 Unprocessable Entity is for when the type was right but the payload is semantically invalid.
Real-World Use Case
A FastAPI POST /users endpoint expecting JSON should return 415 if the request comes with Content-Type: text/plain, even before validation runs. For a content-negotiating Rails app with respond_to do |format|, missing format.html in an HTML-requesting context should return 406, prompting the client to request format.json instead.
Look Up Any Status Code
Related Comparisons
HTTP 400 vs 422 — Bad Request vs Unprocessable Entity Comparison
http 400 vs 422 explained: when to return Bad Request for malformed syntax versus Unprocessable Entity for valid syntax that fails business validation. Includes API examples.
HTTP 405 vs 501 — Method Not Allowed vs Not Implemented Comparison
http 405 vs 501: 405 means this method isn't allowed on this resource, 501 means the server doesn't implement the method at all. With Allow header guidance.
HTTP 414 vs 413 — URI Too Long vs Payload Too Large Comparison
http 414 vs 413: 414 fires when the URL itself is too long, 413 when the request body is too big. Learn typical limits in Nginx, ALB, and Cloudflare.
HTTP 409 vs 412 — Conflict vs Precondition Failed Comparison
http 409 vs 412: both signal state conflicts, but Conflict is for resource-level clashes while Precondition Failed is triggered by If-Match / If-None-Match headers. ETag examples included.