Can HTTP DELETE Have a Body? What the Spec Says and What Actually Works
Engineering

Can HTTP DELETE Have a Body? What the Spec Says and What Actually Works

Yes, an HTTP DELETE request can technically carry a body — but RFC 9110 says clients SHOULD NOT send one, it has no defined semantics, and many clients and servers strip it. Here's what the spec says, what works in practice, and where to put your data instead.
Can HTTP DELETE Have a Body? What the Spec Says and What Actually Works
SO
Sohail Pathan
Last updated on March 18, 2025

Can HTTP DELETE Have a Body? What the Spec Says and What Actually Works

Introduction

An HTTP DELETE request can technically include a body — the protocol's syntax doesn't forbid it. But the current HTTP specification, RFC 9110, states that a client SHOULD NOT send content in a DELETE request, that such content has no generally defined semantics, and that it cannot alter the meaning or target of the request. In plain terms: it's allowed by the grammar, discouraged by the standard, and unreliable in practice.
That gap between "syntactically legal" and "actually works" is the whole story here. You can write the request:
1
curl -X DELETE https://api.example.com/users/123 \
2
-H "Content-Type: application/json" \
3
-d '{"reason": "account closure"}'
The server may read that body, ignore it, or reject the request outright — and all three are spec-compliant. This post covers exactly what the specification says, why bodies get dropped, where to put your data instead, and how to send one safely when an API genuinely expects it.

What the specification actually says

There's a common misconception, repeated in a lot of older articles, that RFC 7231 "permits" a body on DELETE. That's not quite right, and RFC 7231 is no longer the current reference.
RFC 7231 (2014) said only that a payload within a DELETE request has no defined semantics, and that sending one might cause some existing implementations to reject the request. It never "permitted" or endorsed the practice — it acknowledged the syntax allowed it while warning against relying on it.
In June 2022, RFC 7231 was obsoleted by RFC 9110 (HTTP Semantics), which is now the authoritative reference. RFC 9110 uses the term "content" rather than "payload" and is more explicit: content received in a DELETE request has no generally defined semantics, cannot alter the meaning or target of the request, and might lead some implementations to reject it. It adds a normative recommendation — a client SHOULD NOT generate content in a DELETE request.
So the correct answer to "can DELETE have a body" is: yes, nothing in the syntax forbids it, but the current standard actively recommends against it and guarantees nothing about how a server will treat it.

Where to put your data on a DELETE

Because the body can't reliably alter what gets deleted, the data that identifies what to delete belongs elsewhere. Here's where each kind of information should go:
WhereUse it forReliability
URL pathThe resource identifier — /users/123Universally supported
Query parametersFilters and options — ?soft=true, ?before=2026-01-01Universally supported
HeadersAuth (Authorization), conditional deletes (If-Match)Universally supported
Request bodyComplex or bulk criteria an API explicitly documentsNot reliable — may be stripped or rejected
The rule of thumb: the resource identifier goes in the path, options go in query parameters, and the body stays empty unless the API's own documentation tells you it expects one.

Sending a body in practice

If an API does document a DELETE body (some bulk-delete or complex-criteria endpoints do), here's how the common clients handle it.
curl — pass the body with -d and set the content type:
1
curl -X DELETE https://api.example.com/users \
2
-H "Content-Type: application/json" \
3
-d '{"ids": [123, 124, 125]}'
Axios — this one has a gotcha: axios.delete() doesn't take a body as its second argument the way post does. The body must go under the data key in the config object:
1
import axios from 'axios';
2
3
await axios.delete('https://api.example.com/users', {
4
headers: { 'Content-Type': 'application/json' },
5
data: { ids: [123, 124, 125] }
6
});
Fetch — DELETE bodies are allowed (only GET and HEAD are forbidden from carrying one), so this works as expected:
1
await fetch('https://api.example.com/users', {
2
method: 'DELETE',
3
headers: { 'Content-Type': 'application/json' },
4
body: JSON.stringify({ ids: [123, 124, 125] })
5
});

What the response looks like

A successful DELETE usually returns one of two things. Most commonly, 204 No Content — success with a deliberately empty body:
1
HTTP/1.1 204 No Content
Some APIs return 200 OK with a small confirmation payload instead:
1
HTTP/1.1 200 OK
2
Content-Type: application/json
3
4
{"deleted": true, "id": "123"}
Either is valid. Just don't try to parse a body on a 204 — there isn't one, and doing so will throw. For the full picture of what each status code means, see the HTTP status codes cheat sheet.

Why clients and servers strip DELETE bodies

The reason "SHOULD NOT" exists in the spec is that a DELETE body doesn't survive the trip reliably. Anything in the request path can drop it:
  • HTTP client libraries may not send it. Because the semantics are undefined, some libraries omit a DELETE body by default or make it awkward to attach (Axios needing the data key is one example).
  • Proxies, gateways, and CDNs sitting in front of the origin can strip the body, since intermediaries are free to ignore content with no defined meaning.
  • Servers and frameworks may not parse it, or may reject the request entirely.
This isn't theoretical. A recent bug report against the Ollama project pointed out that its delete endpoint put the model name in the DELETE request body, which caused several programming languages and third-party libraries to mishandle the request — the fix was to move the identifier into the URL path, exactly as the spec advises. If a widely-used tool hits this, so will your integration.

Framework support, briefly

Most server frameworks can read a DELETE body if you configure them to, because body parsing is usually keyed on the Content-Type header, not the HTTP method:
  • Express parses a DELETE body once express.json() (or the older body-parser) is registered — it parses by content type, so a JSON DELETE body is available on req.body.
  • Spring supports @RequestBody on a @DeleteMapping, though it's an unusual pattern worth documenting for whoever consumes the endpoint.
  • Django REST Framework exposes the parsed body on request.data for DELETE like any other method.
The fact that your framework can read it doesn't mean the clients calling you will send it, or that a proxy won't strip it first. Support on one end isn't support end to end.

Best practices

  1. Default to an empty DELETE body. Put the resource identifier in the URL path and any options in query parameters. This works everywhere and matches the spec's recommendation.
  2. Only send a body when the API explicitly documents one. If it isn't in the API's specification, assume the body will be ignored or rejected.
  3. If you must send one, verify the whole chain. Test that your client library, any proxy or CDN, the gateway, and the server all preserve the body — not just one of them.
  4. Never rely on the body to change the target. The spec is explicit that DELETE content can't alter what the request acts on, so encode the target in the path.
  5. Document it in your OpenAPI spec. If your own API accepts a DELETE body, declare it so consumers and AI agents calling the endpoint know it's expected rather than incidental.

Where ApyHub Fits

Most of the pain around DELETE bodies comes from guessing — does this endpoint want the criteria in the body, the query string, or the path? That's a documentation problem.
ApyHub is a certified API marketplace: a catalog of 200+ APIs and 1,000+ endpoints for common utility work, where every endpoint ships with a documented, machine-readable specification — including exactly how it expects its requests shaped. When you consume an endpoint from the catalog, whether it takes a body and where identifiers go is declared up front, for humans reading the docs and for AI agents calling it over MCP. It doesn't change what the HTTP spec says about DELETE; it removes the guesswork about what a specific endpoint actually expects.
If you want to test whether a DELETE body survives your request chain before you ship, Voiden — an offline-first, Git-native API client that stores requests as version-controlled .void files — lets you commit the exact call and reproduce it across environments rather than debugging it locally in isolation.

Conclusion

Can an HTTP DELETE have a body? Technically yes — but RFC 9110 says clients SHOULD NOT send one, the content carries no defined meaning, and clients, proxies, and servers routinely drop it. The reliable pattern is the boring one: identifier in the path, options in query parameters, body empty, unless an API's own documentation explicitly says otherwise. Follow that and your DELETE requests behave the same way everywhere, instead of working on your machine and failing behind a proxy.

FAQ

Can an HTTP DELETE request have a body?

Yes, the HTTP syntax doesn't forbid it. But RFC 9110, the current specification, says clients SHOULD NOT send content in a DELETE request, that the content has no generally defined semantics, and that some implementations may reject it. Treat a DELETE body as unsupported unless an API documents one.

Does the DELETE body affect what gets deleted?

No. RFC 9110 states that content in a DELETE request cannot alter the meaning or target of the request. The resource being deleted must be identified by the URL, not by the body.

Where should I put data on a DELETE request instead of the body?

Put the resource identifier in the URL path (/users/123) and any filters or options in query parameters (?soft=true). These are universally supported, whereas a body may be stripped by a client library, proxy, or server.

Why do some servers reject a DELETE with a body?

Because the body has no defined semantics, servers, proxies, and gateways are free to ignore or reject it. Some client libraries also drop it by default, so a DELETE body can silently disappear anywhere along the request path.

How do I send a DELETE body in Axios?

Axios doesn't accept a body as the second argument of axios.delete(). You have to pass it under the data key in the config object, alongside headers. This trips up a lot of developers who expect it to work like axios.post().

What status code does a successful DELETE return?

Most commonly 204 No Content, which is a success with an empty body, or 200 OK with a small confirmation payload. Don't try to parse a body on a 204 — see the HTTP status codes cheat sheet for the full breakdown.

Is a DELETE body different from a 405 Method Not Allowed error?

Yes — they're unrelated. A DELETE body question is about request content; a 405 Method Not Allowed means the endpoint doesn't accept the DELETE method at all. If DELETE isn't supported on a resource, you'll get a 405 regardless of whether you send a body.