Why a proxy company writes about status codes: we are Geonode and people route API traffic through us, so we get asked whether a given error is "the proxy". For 415, the answer is essentially always no. A 415 comes from the origin server and describes something about the request you constructed. An intermediary can produce one in narrow circumstances — a filtering proxy inspecting bodies, a gateway with its own content rules — but that is uncommon and it announces itself in the response headers. If you are getting a 415 through a proxy, remove the proxy and you will almost certainly get the same 415 directly. Fix the request. The one status code that genuinely does implicate a proxy is 407, and it says so in the name.
Now, the actual error.
What the Specification Says
RFC 9110, Section 15.5.16, defines it precisely:
The 415 (Unsupported Media Type) status code indicates that the origin server is refusing to service the request because the content is in a format not supported by this method on the target resource.
Three parts of that sentence do work.
"The content" — the request body, not the URL, not the query string, not the response. If your request has no body, a 415 is unusual and suggests something else is going on.
"Not supported by this method" — support is per method. A resource may accept application/json on POST and reject it on PATCH, which is a genuinely common source of confusion when the same endpoint behaves differently under different verbs.
"On the target resource" — and per resource. One endpoint on an API accepting a format tells you nothing about another.
The specification then names the causes:
The format problem might be due to the request's indicated Content-Type or Content-Encoding, or as a result of inspecting the data directly.
That last clause matters and is widely overlooked. A server is permitted to return 415 after looking at the bytes, not merely after reading the headers. Declaring Content-Type: application/json and sending something that is not JSON can legitimately produce a 415 rather than a 400.
The RFC also specifies what the server should tell you. If the problem was the content coding, it says the Accept-Encoding response header "ought to be used to indicate which (if any) content codings would have been accepted". If it was the media type, Accept "can be used to indicate which media types would have been accepted". In practice, MDN documents that servers commonly use Accept-Post and Accept-Patch for the method-specific cases, which is more useful still.
Read the response headers. Servers frequently tell you the answer and clients frequently discard it.
The Six Things That Cause It
In rough order of how often we see them.
1. Missing Content-Type entirely. You send a body and never declare its format. Many frameworks will not guess. MDN's example is exactly this: a POST with a JSON body, a Content-Length, and no Content-Type, answered with 415 and Accept-Post: application/json; charset=UTF-8.
2. The wrong Content-Type. The classic is sending JSON while declaring application/x-www-form-urlencoded, usually because an HTTP client defaults to form encoding and you passed a JSON string without changing it. The body is fine; the label is wrong.
3. A close-but-wrong media type. text/json instead of application/json. application/xml where the server wants text/xml. Vendor types such as application/vnd.api+json where you sent plain application/json. Strict servers do exact matching and will not be charitable.
4. Charset problems. MDN gives the sharpest example: sending UTF8 where the server requires UTF-8. The hyphen is not optional in the registered name, and a server doing strict parameter validation is within its rights to reject it.
5. Content-Encoding the server does not support. You gzip the request body and set Content-Encoding: gzip against a server that only handles identity encoding. The RFC anticipates this case specifically, and a well-behaved server should return Accept-Encoding telling you what it would take.
6. The body does not match the declared type. Correct header, wrong bytes — often a serialisation bug, or a template that emitted an empty string, or a body that got double-encoded somewhere in the stack. This is the "inspecting the data directly" clause in action.
415 Versus 406 Versus 400 Versus 422
This is the confusion map, and the specification distinguishes them cleanly.
| Code | What it means | Direction | Fix by changing |
|---|
| 415 | The format you sent is unsupported | Request body | Content-Type or Content-Encoding |
| 406 | No representation you accept is available | Response | Accept header |
| 400 | The request is malformed | Whole request | Syntax or framing |
| 422 | Format understood, content unprocessable | Request body | The data itself |
| 413 | Body too large | Request body | Payload size |
415 versus 406 is a direction problem and the easiest to get right once stated. 415 is about what you sent. 406 is about what you asked to receive — RFC 9110 defines it as the resource not having "a current representation that would be acceptable to the user agent, according to the proactive negotiation header fields received". If you are getting 406, look at your Accept header, not your body.
415 versus 400. RFC 9110 describes 400 as the server not processing the request "due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing)". 400 is structural — the request itself is broken. 415 is a well-formed request whose body is in a format the server will not take. In practice many servers return 400 where 415 would be more precise; you cannot control this, so treat a 400 on a request with a body as possibly a 415 in disguise.
415 versus 422 is the distinction the RFC draws most explicitly. Section 15.5.21 says 422 indicates "the server understands the content type of the request content (hence a 415 (Unsupported Media Type) status code is inappropriate), and the syntax of the request content is correct, but it was unable to process the contained instructions."
So the ladder is: 415 means the wrapper is wrong, 422 means the wrapper is right and the contents are wrong. Well-formed JSON with a missing required field is a 422. The same JSON labelled as form data is a 415.
Diagnosing It in Under Two Minutes
A fixed sequence, which resolves nearly all cases.
Step one: read the response headers. Not the status line, the headers.
curl -i -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-d '{"name":"test"}'
Look for Accept, Accept-Post, Accept-Patch or Accept-Encoding in the response. If any is present, it is a direct statement of what the server wants and you are done.
Step two: confirm what you actually sent. Not what you meant to send — what went on the wire. Client libraries add, override and reformat headers, and the header you set in code is not always the header that was transmitted.
curl -v -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-d '{"name":"test"}' 2>&1 | grep '^>'
The > lines are your actual request. A surprising share of 415s are resolved right here, when the Content-Type you carefully set turns out to have been overwritten by a default.
Step three: check the method. The same endpoint can accept a type on POST and reject it on PATCH. Try the same body under a different verb and see whether the behaviour changes.
Step four: check the exact type string. Compare character by character against the documentation. application/json versus text/json. UTF-8 versus UTF8. Vendor suffixes. This is tedious and it is where the answer often is.
Step five: read the documentation for that specific endpoint. APIs are not uniform internally. A file upload endpoint wanting multipart/form-data in an otherwise JSON API is entirely normal.
Fixing It on the Client Side
The common cases in the common clients.
curl. -d implies application/x-www-form-urlencoded unless you say otherwise. This is the single most common cause of a 415 from the command line:
curl -X POST https://api.example.com/items \
-H "Content-Type: application/json" \
-d '{"name":"test"}'
JavaScript fetch. Passing a string body sets no Content-Type at all:
await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "test" }),
});
The exception worth knowing: with FormData, do not set Content-Type yourself. The browser must generate it because it includes the multipart boundary, and overriding it produces a broken request that frequently manifests as a 415.
Python requests. Use json= rather than data= and the header is set for you:
requests.post(url, json={"name": "test"}) # application/json
requests.post(url, data={"name": "test"}) # form-encoded
Axios. Sets application/json for plain objects and something else for strings, which is a frequent source of surprise. If you have already serialised your payload, set the header explicitly. The behavioural differences between clients here are exactly the kind of thing we covered in axios vs fetch.
A general rule: when a client offers a JSON-specific parameter, use it rather than serialising by hand and hoping the default header is right.
Returning It Correctly on the Server Side
If you are on the other end, a few things make your API considerably easier to work with.
Send an Accept-Post or Accept-Patch header with the 415. The RFC asks for Accept or Accept-Encoding; the method-specific variants are more precise and MDN documents them for exactly this use. This single header converts a debugging session into a glance.
Include a readable body. A status code with an empty body forces the client to guess. Say what you received and what you expected.
Distinguish 415 from 422 correctly. If you understood the content type and the body parsed but failed validation, that is a 422. Returning 415 for validation failures sends people to check their headers when their headers were fine, and it is a common and costly mistake in API design.
Be forgiving about parameters where you safely can. Rejecting application/json; charset=utf-8 when you accept application/json is technically defensible and practically unhelpful. Parse the media type properly and ignore parameters you do not care about.
Do not use 415 as a generic rejection. It has a specific meaning. Overloading it makes your API harder to use and makes client-side retry logic wrong.
When a 415 Is Not Really a 415
Cases where the status code misleads.
A gateway or WAF rejected the request. Some security layers return 415 for bodies they consider suspicious, regardless of the actual content type. The clue is usually a response body that does not look like it came from the application, or headers identifying an intermediary.
A framework default fired before your code ran. Many web frameworks reject unknown content types in middleware. Your handler never executed, so nothing in your application logic is relevant to the fix.
A load balancer or CDN stripped a header. Rare but real. If the request works when sent directly and fails through infrastructure, compare the headers at both ends before assuming the application changed.
The endpoint does not exist. Some servers respond to an unmatched route on a POST with 415 rather than 404, because content-type negotiation happens before routing resolves. Check the URL.
HTTP method override went wrong. If your framework supports overriding the method via a header or query parameter, the effective method may not be the one you sent, and content type support is per method.
In every one of these, the fix is upstream of your payload. The general principle: if the request is obviously correct and the 415 persists, stop editing the body and start finding out which component in the path is producing the response.
People Also Ask
What does 415 Unsupported Media Type mean?
The server refused the request because the format of the request body is not supported for that method on that resource. RFC 9110 attributes it to the Content-Type header, the Content-Encoding header, or the server inspecting the body directly. It is about the format of what you sent, not the correctness of the data.
How do I fix a 415 error?
Check the response headers first — servers frequently return Accept, Accept-Post or Accept-Encoding naming exactly what they want. Then verify what your client actually transmitted, since libraries override headers. The most common single fix is adding Content-Type: application/json to a request that had none.
What is the difference between 415 and 400?
400 means the request is malformed — bad syntax or framing. 415 means the request is well formed but the body is in an unsupported format. In practice servers often return 400 where 415 would be more precise, so a 400 on a request with a body is worth investigating as a possible content-type problem.
What is the difference between 415 and 422?
RFC 9110 draws this line explicitly: 422 means the server understood the content type and the syntax was correct but it could not process the instructions. So 415 is the wrapper being wrong; 422 is the contents being wrong. Valid JSON missing a required field is a 422.
Why do I get 415 when uploading files?
Usually because Content-Type was set manually on a multipart request. The browser or client must generate that header itself, because it contains the multipart boundary. Setting it yourself removes the boundary and produces a request the server cannot parse.
Can a proxy cause a 415?
Rarely. 415 comes from the origin server and describes your request body. A filtering proxy or gateway inspecting content can produce one, but the usual proxy-specific status code is 407, which says so in its name. Test without the proxy — if the 415 persists, the proxy was not involved.
Does 415 mean my JSON is invalid?
Not necessarily. If the server rejected the content type, your JSON was never examined. If the server declared the right type and then found the body was not actually that format, then yes — the RFC permits rejecting after "inspecting the data directly". Check the header question first; it is far more common.
Should I retry after a 415?
No. It is a client error and the same request will produce the same result. Retrying wastes requests and, if you are rate limited, may make things worse. Fix the content type and send once.
Wrapping Up
415 has a narrow and precise meaning: the wrapper around your data is not one this endpoint accepts for this method. It is not about your data being invalid, which is what 422 is for, and it is not about what you asked to receive, which is what 406 is for.
Because the meaning is narrow, the diagnosis is short. Read the response headers, because a well-behaved server names the acceptable types in Accept, Accept-Post or Accept-Encoding. Then verify what your client actually put on the wire rather than what you told it to, since defaults and middleware routinely override the header you set. Between those two steps you will resolve most cases without touching the body at all.
And if the request looks unimpeachable and the 415 persists, the response is probably not coming from the application you think it is. Gateways, framework middleware and unmatched routes all produce 415s that have nothing to do with your payload — at which point the useful question is not what to change, but which component in the path is answering.