Understanding HTTP 204 No Content: Why It's Not 200 With an Empty Body
204 No Content is the status code for "it worked, and there's nothing more to say." That sounds like a shrug, but it's a deliberate one: 204 doesn't mean the server forgot to send a body, or that something went slightly wrong along the way — it means the server is telling the client, on purpose, that no body is coming. The most common mistake with 204 is skipping it entirely and returning 200 with an empty JSON object instead, which throws away that signal. This post covers what 204 actually promises, why it's the right choice for DELETE and body-less PUT, how it compares to its 2xx siblings, and how to return it correctly without accidentally violating the spec.
What Is a 204?
A 204 means the request succeeded and the server has nothing further to send in the response body — not "nothing to send this time," but "nothing to send, ever, for this response."
The 204 (No Content) status code indicates that the server has successfully fulfilled the request and that there is no additional content to send in the response payload body. — RFC 9110, Section 15.3.5
In plain English: the operation succeeded, but there's intentionally nothing to send back. It's most at home on DELETE requests (the thing is gone — what would the body even contain?) and on PUT/PATCH requests where the client already knows the resulting state and doesn't need it echoed back. A 204 response should not include a message body at all.
204 Is Not "200 With Nothing in It"
This is the distinction that gets lost, and it's the single most common mix-up around this code — as our own 204 reference page puts it:
Some developers use 200 with an empty body instead of 204. Use 204 when there's intentionally no content to return — it's more semantically correct and saves bandwidth by not requiring a
Content-Lengthheader or message body.
A 200 OK with {} or an empty string says "here is your content, which happens to be zero-length" — it implies a body was expected and this is what came back. A 204 No Content says "success, and there was never going to be a body." The difference isn't pedantic: it's the difference between a client that has to check Content-Length before deciding whether to parse a response, and a client that already knows from the status code alone that there's nothing to parse. Content-Length, if present at all on a 204, must be 0 — most HTTP libraries strip it automatically because a 204 response is, by definition, terminated at the first blank line after the headers.
204 vs 200 vs 201 vs 202 vs 205
Five success codes cover different shapes of "it worked":
| Code | Meaning | Use when |
|---|---|---|
| 200 OK | Generic success, with body | Reads, updates, and any success where you're returning a representation |
| 201 Created | New resource exists | A POST or PUT brought a resource into existence — pair with Location |
| 202 Accepted | Queued, not done | The request is valid and accepted, but processing finishes later |
| 204 No Content | Success, nothing to say | Deletes, or updates where the client needs no body back |
| 205 Reset Content | Success, reset the view | Like 204, but tells the client to reset the form/view that sent the request |
204 and 205 are close cousins — both mandate an empty body — but they carry different instructions to the client. 204 says "nothing more to do." 205 says "nothing more to do, and clear whatever form or view triggered this request." 205 is rare in practice; if you're not sure which one you want, you almost certainly want 204.
Common Causes
204 shows up in a short list of genuinely common situations:
- Successfully deleted a resource — the classic case. Once something is deleted, there's nothing left to represent in a response body.
- An update that doesn't need to return data — a
PUTorPATCHwhere the client already has (or doesn't need) the resulting object. Returning it anyway is fine, but it's not required, and 204 is the honest signal when you don't. - A preflight CORS request succeeded — browsers send an
OPTIONSpreflight before certain cross-origin requests, and since that request exists purely to negotiate permissions, servers commonly answer it with 204: the preflight isn't asking for a representation, so there's nothing to send back.
Returning 204 Correctly
Express / Node.js
app.delete("/api/users/:id", (req, res) => {
db.deleteUser(req.params.id);
// 204 — success, nothing to return
res.status(204).send();
});
// Equivalent shorthand: sets the status and sends the standard reason phrase
app.put("/api/users/:id/archive", (req, res) => {
db.archiveUser(req.params.id);
res.sendStatus(204);
});Express strips Content-Type, Content-Length, and Transfer-Encoding from any response whose status is 204 (or 304) before it goes out, so you don't need to manually clear headers you set earlier in the handler — Express does it for you at send time.
Next.js App Router
In a Route Handler, return a bare Response with null as the body and no Content-Type:
// app/api/users/[id]/route.ts
export async function DELETE(
request: Request,
{ params }: { params: Promise<{ id: string }> },
) {
const { id } = await params;
await deleteUser(id);
return new Response(null, { status: 204 });
}If you don't define an OPTIONS handler for a route, Next.js automatically implements one and sets the appropriate Allow header for you — worth knowing if you're building an API that browsers will call cross-origin and relying on preflight responses to come back clean.
NGINX
location /api/healthcheck {
return 204;
}What NOT to send
Unlike most status codes, there's no "REST API JSON body" example for 204 — that's the point. Sending a body alongside a 204 status violates the spec, and well-behaved clients will ignore it or treat it as an error while parsing. The correct wire response is just the status line and headers, nothing after the blank line:
HTTP/1.1 204 No ContentCommon Pitfalls
- Returning
200with{}instead of 204. It "works" for most clients, but it throws away the semantic signal that nothing was ever coming, and it costs aContent-Length: 2and a body-parsing step for no reason. - Attaching a body to a 204 anyway. Some middleware or serializers add a body reflexively (
res.json(deletedRecord)after setting status 204). Most HTTP stacks will strip it, but don't rely on that silently happening — set the status and skip the body call. - Using 204 when the client actually needs something back. If a
PUTgenerates a new value the client couldn't have predicted (a server-assigned timestamp, a recalculated total), return 200 with that data instead of 204 — don't force a follow-upGET. - Treating 204 as an error in client code. It's a 2xx success code. Client code that only special-cases exact
200responses and treats everything else as a failure will misreport successful deletes and updates as errors. - Forgetting 204 on CORS preflight paths. If your
OPTIONShandler returns something other than a 2xx status (or omits the CORS headers the browser is checking for), the browser cancels the real request before it's ever sent — a bug that's invisible in server logs because the actual request never arrives.
Wrapping Up
204 is small, but it's a genuine signal, not an empty gesture. The rules of thumb:
- 204 means success, and no body was ever coming — that's different from a 200 that happens to be empty
- Reach for it on
DELETEs and on updates where the client doesn't need the result echoed back - Never attach a body to a 204 response; strip
Content-TypeandContent-Lengthif your framework doesn't do it for you - If the client genuinely needs data back, that's a 200, not a 204 with a follow-up request
For more, see our pages on 204 No Content, 200 OK, and 201 Created. The full family of 2xx success codes — including when 201 beats 200, and when 202 is the honest answer for async work — is covered in our understanding 200 OK and understanding 201 Created posts.