Why 415 Unsupported Media Type Happens (and How to Fix It Fast)
Compare form and JSON requests for a JSON endpoint, verify the response, and distinguish browser FormData boundaries from hand-built multipart bodies.
HTTP 415 means the server rejected the request content format for this method and resource. Start with the endpoint’s accepted media types, the actual body, Content-Type, and Content-Encoding. A valid format for one endpoint can be unsupported by another.
Tools in this guide
Symptoms
- An endpoint returns 415 after receiving a request body.
- The endpoint works in one client but rejects a request from a frontend or SDK.
- A multipart upload is rejected even though the client selected a file.
Root Cause
- The route does not accept the body’s media type, or Content-Type does not describe the actual body.
- Content-Encoding declares a coding the route does not accept, or the declared coding does not match the bytes.
- A multipart boundary is missing or inconsistent with the body, or a gateway rewrites relevant headers.
Fix Steps
- Confirm the method, endpoint, accepted media types, and required fields in the API documentation.
- Compare the outgoing header and body together; use the JSON request comparison below only if the endpoint accepts that JSON shape.
- For multipart, identify whether the browser, a library, or your own code constructs the body; keep its boundary and Content-Type together.
- Retest in your HTTP client, read the response, and verify the documented outcome. ToolsKit’s request builder generates code and does not send the request.
Form request → JSON request (placeholder API)
# Example contract: POST /messages accepts JSON {"message":"hello"}.
# api.example.invalid is a placeholder; use your own matching test endpoint.
# WRONG for this contract — commented out so copying does not run it:
# curl -i 'https://api.example.invalid/messages' -H 'Content-Type: application/x-www-form-urlencoded' --data-raw 'message=hello'
# CORRECT media type and body for the example contract:
curl -i 'https://api.example.invalid/messages' -H 'Content-Type: application/json' --data-raw '{"message":"hello"}'
# Check the response status, body, and expected server-side result.Related tools
FAQ
Can 415 happen even when JSON is valid?
Yes. The route may not accept application/json, or it may reject the declared content coding. Valid JSON syntax alone does not establish compatibility with that endpoint.
Should I set charset for JSON?
RFC 8259 requires UTF-8 for JSON exchanged outside a closed ecosystem and defines no charset parameter for application/json. Use application/json; follow a documented API-specific requirement if it differs.
Read the example against the endpoint contract
The example assumes a test endpoint that accepts application/json with a string field named message. The first, commented-out command sends valid URL-encoded form data instead. That is the wrong media type for this assumed contract; it is not an inherently invalid request format. The second command changes both Content-Type and the body to JSON. Sending message=hello while merely relabeling it application/json would still leave a non-JSON body.
api.example.invalid is a documentation placeholder and is not a working API. Replace it only with a test endpoint you control that supports the stated method and fields, and supply any authentication your endpoint requires. Keep the message field and body format consistent with your endpoint’s documented request shape.
A request’s Content-Type describes the body you are sending. Accept expresses the response media types the client can handle; changing Accept to application/json does not turn a form body into JSON. Content-Encoding describes an applied coding such as gzip. Do not set it to gzip unless the outgoing bytes are actually gzip-encoded and the endpoint accepts that coding.
Confirm the result instead of only watching the status
Use your HTTP client’s outgoing-request view, or the browser Network panel, to compare method, URL, Content-Type, Content-Encoding, and the actual body before and after the change. The sample curl command uses -i to show response headers and the response body; that output alone does not show what a gateway forwarded to the origin. If rewriting is suspected, compare with the request received by your application.
Read the returned status and error details, then verify the endpoint’s documented outcome: for this example, the server must parse message as the string hello and handle it as intended. A 2xx response or the disappearance of 415 alone does not establish that the desired record or action is correct. A new 400, 401, or 422 may point to parsing, authentication, or field validation; use the actual response and server logs because applications choose status codes differently.
If 415 remains, check whether this route accepts the selected media type or content coding at all. A 415 response may advertise acceptable request media types with Accept, or acceptable request codings with Accept-Encoding. Those hints are optional and do not replace the endpoint documentation or the received-request evidence.
Choose who constructs the multipart body
Use multipart/form-data only when the endpoint expects it. When passing a FormData object as the body of fetch or XMLHttpRequest, let the browser generate the request Content-Type and its boundary. Remove an inherited application/json header from your request wrapper as well; setting multipart/form-data yourself without the matching boundary prevents the browser’s normal header from describing the body correctly.
When a client or library constructs multipart, let that same component produce both the header and the body. If you construct the bytes manually, boundary=demo-boundary in Content-Type must correspond to --demo-boundary delimiter lines in the body and the closing --demo-boundary-- delimiter. Use the required CRLF separators and a blank line between each part’s headers and content; each form-data part needs Content-Disposition: form-data with a name parameter. The delimiter must not occur inside a part. Copying a boundary from another request does not rebuild these bytes.
Content-Type Parser can inspect the header text you paste; it cannot verify body delimiters or discover what your server accepts. Content-Type Generator suggests a header, and HTTP Request Builder generates code. Run the resulting request in your own client and inspect the actual response.
Protocol references and browser guidance
RFC 9110 §15.5.16 — 415 Unsupported Media Type
Defines rejection by media type, content coding, or inspection of the request data, and explains the optional response hints.
RFC 7578 §4.1–4.2 — multipart boundaries and part headers
Specifies the boundary relationship and the required Content-Disposition field for form-data parts.
MDN — Using FormData Objects
Explains why fetch and XMLHttpRequest requests with FormData should let the browser set Content-Type and the boundary.
RFC 8259 — JSON encoding and media type
Section 8.1 specifies UTF-8 for JSON exchanged outside a closed ecosystem; section 11 registers application/json without a charset parameter.