HTTP 204 No Content

HTTP 204 No Content indicates the server has successfully fulfilled the request and there is no additional content to send in the response body. The response MUST NOT include a body. This status is the correct choice for successful DELETE operations, PUT/PATCH updates where the client doesn't need the updated resource back, and actions like 'mark as read' or 'toggle favorite' where the outcome is binary. The key distinction from 200 OK with an empty body is that 204 explicitly tells the client 'there is intentionally no content here.'

Debug HTTP 204 live
Analyze real 204 behavior — headers, caching, CORS, redirects
Open Inspector →

Try it (live endpoint)

Response includes the status code, standard headers (including Content-Type), and a small diagnostic JSON body describing the request and returned status.

Simulator URL (copy in the app after load — not a normal link):

https://httpstatus.com/api/status/204

Example request:

curl -i "https://httpstatus.com/api/status/204"
Try in playground

Meaning

The server successfully processed the request and is not returning any content.

What it guarantees
  • The server accepted the request and produced a final response.
  • There is intentionally no response body.
What it does NOT guarantee
  • The underlying business operation is correct across all downstream systems.
  • The response is cacheable unless headers explicitly allow it.
  • A client will receive a response body.

When to use this status

  • PUT/PATCH updates succeed and you intentionally return no representation.
  • DELETE succeeds and there is no payload to return.
  • State-change endpoints where clients don’t need a response body.

When NOT to use this status (common misuses)

Returning 200 for partial failures or errors embedded only in the body.
Clients and monitors treat it as success; failures become silent and harder to alert on.
Returning 200 for creation instead of 201 with Location.
Clients lose a reliable created-resource identifier; SDK behavior becomes inconsistent.
Returning 200 for async acceptance instead of 202.
Clients assume the work is complete and proceed incorrectly.
Returning a response body with 204.
Some clients/proxies mis-handle it; connection reuse and serializers can break.

Critical headers that matter

Content-Type
Defines how clients parse the body.
Clients mis-parse payloads; SDKs and browsers apply wrong decoding.
Cache-Control
Controls cacheability and revalidation.
CDNs/browsers cache dynamic data or fail to cache static content.
ETag / Last-Modified
Enables conditional requests and revalidation.
Unnecessary bandwidth; poor cache consistency.
Content-Length
Should typically be 0 for no-body responses.
Some clients wait for bytes or mis-handle connection reuse.

Tool interpretation

Browsers
Treats as success; caches/revalidates based on headers and validators.
API clients
Deserializes per Content-Type; conditional requests use validators when implemented.
Crawlers / SEO tools
Indexes depending on headers and canonical stability; caches behavior via validators and cache directives.
Uptime monitors
Typically marks success; advanced checks may flag header anomalies or latency.
CDNs / reverse proxies
Caches/revalidates based on Cache-Control, ETag, and Vary; compression and content-type affect behavior.

Inspector preview (read-only)

On this code, Inspector focuses on semantics, headers, and correctness warnings that commonly affect clients and caches.

Signals it will highlight
  • Status semantics vs method and body expectations
  • Header sanity (Content-Type, Cache-Control, Vary) and evidence completeness
Correctness warnings
  • Body present with 204 is incorrect and may break clients/proxies.

Guided Lab outcome

  • Reproduce HTTP 204 No Content using a controlled endpoint and capture the full exchange.
  • Practice distinguishing status semantics from transport issues (redirects, caching, proxies).

Technical deep dive

204 No Content (RFC 7231 Section 6.3.5) serves two purposes: confirming success and signaling that the current document view should not change. Browsers receiving 204 from a form submission stay on the current page rather than navigating. The response may include headers (like ETag for the updated resource state) but MUST NOT have a body — not even an empty JSON object. A 204 response is terminated by the first empty line after the headers, with no transfer-encoding or content-length. Important nuance: 204 from a DELETE means the resource was deleted; 204 from a PUT means the update was applied. For CORS preflight (OPTIONS), 204 is preferred over 200 because the browser doesn't need a body.

Real-world examples

REST API DELETE
DELETE /api/users/42 successfully removes the user. The server returns 204 No Content — no body is needed because the resource no longer exists. Some APIs prefer 200 with the deleted resource for audit logging, but 204 is the most semantically precise.
CORS preflight response
OPTIONS /api/data returns 204 with Access-Control-Allow-Origin, Access-Control-Allow-Methods, and Access-Control-Max-Age headers. 204 is preferred over 200 for preflight because no body is needed and it saves bandwidth on what is effectively a protocol-level handshake.
Analytics event tracking
POST /api/events sends a page view event. The server stores it and returns 204 — the client fire-and-forget pattern. No response data is needed because the client doesn't use the result. A single-pixel GIF would be the alternative for cross-origin tracking without CORS.

Framework behavior

Express.js (Node)
Express: res.status(204).end() — do NOT use res.json() as it would send a body. Also: res.sendStatus(204). For CORS preflight: if (req.method === 'OPTIONS') res.sendStatus(204);
Django / DRF (Python)
Django: return HttpResponse(status=204). With DRF: return Response(status=status.HTTP_204_NO_CONTENT). DRF's DestroyModelMixin automatically returns 204 for successful DELETE.
Spring Boot (Java)
Spring: @DeleteMapping("/{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void delete(@PathVariable Long id) { ... }. Or: return ResponseEntity.noContent().build(); The void return type ensures no body serialization.
FastAPI (Python)
FastAPI: @app.delete('/items/{id}', status_code=204). Important: the handler should return None or use Response(status_code=204). If the handler returns a value with 204, FastAPI may still try to serialize it.

Debugging guide

  1. If clients crash parsing a 204 response, they're likely trying to JSON.parse an empty body — add null checks before parsing
  2. Check Content-Length is 0 or absent — a Content-Length > 0 with no body causes client issues
  3. If browsers navigate away on 204, you might be sending it from a form action — 204 tells browsers to stay on the current page
  4. For CORS issues, verify the preflight OPTIONS returns 204 with correct Access-Control-* headers
  5. Test with curl -v to verify no body bytes are sent after the headers

Code snippets

Node.js
// DELETE with 204
app.delete('/api/users/:id', async (req, res) => {
  const deleted = await db.deleteUser(req.params.id);
  if (!deleted) return res.status(404).json({ error: 'Not found' });
  res.status(204).end(); // No body
});

// CORS preflight
app.options('/api/*', (req, res) => {
  res.set('Access-Control-Allow-Origin', req.headers.origin);
  res.set('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
  res.set('Access-Control-Max-Age', '86400');
  res.sendStatus(204);
});
Python
from fastapi import FastAPI, Response

@app.delete('/api/users/{user_id}', status_code=204)
async def delete_user(user_id: int):
    deleted = await db.delete_user(user_id)
    if not deleted:
        raise HTTPException(404, 'User not found')
    return Response(status_code=204)  # explicit no body

@app.put('/api/users/{user_id}/read', status_code=204)
async def mark_read(user_id: int):
    await db.mark_read(user_id)
    # Return None implicitly → 204 No Content
Java (Spring)
@DeleteMapping("/api/users/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
    if (!userRepo.existsById(id)) {
        throw new ResponseStatusException(HttpStatus.NOT_FOUND);
    }
    userRepo.deleteById(id);
    return ResponseEntity.noContent().build(); // 204
}
Go
func deleteUserHandler(w http.ResponseWriter, r *http.Request) {
	id := chi.URLParam(r, "id")
	deleted, err := db.DeleteUser(id)
	if err != nil {
		http.Error(w, "Internal error", 500)
		return
	}
	if !deleted {
		http.Error(w, "Not found", 404)
		return
	}
	w.WriteHeader(http.StatusNoContent) // 204, no body
}

FAQ

Should DELETE return 204 No Content or 200 with the deleted resource?
Both are valid. 204 is more RESTful — the resource is gone, so returning it is paradoxical. 200 with the deleted resource is useful for undo functionality or audit trails. Choose based on your API's needs. Never return 204 from a DELETE that didn't actually delete anything — use 404 instead.
Can a 204 response include headers?
Yes. A 204 response may include any headers. Common examples: ETag (reflecting the state after the update), Cache-Control (to invalidate caches), and X-Request-Id (for correlation). Only the response body must be empty.
Why use 204 instead of 200 with an empty body?
204 is an explicit contract: 'there is no content and the client should not expect any.' 200 with an empty body is ambiguous — is the empty body intentional or a bug? 204 also tells browsers not to navigate away from the current page, while 200 with empty body may cause blank page rendering.
How do JavaScript fetch and XMLHttpRequest handle 204?
fetch() resolves successfully (response.ok is true). response.json() will throw because there's no body to parse — always check response.status === 204 before parsing. XMLHttpRequest sets status to 204 and responseText to empty string. Axios resolves with data as empty string for 204.

Client expectation contract

Client can assume
  • A final HTTP response was produced and processed by the server.
Client must NOT assume
  • The change is durable across all downstream systems.
Retry behavior
Retries are generally unnecessary; treat as final unless domain rules require revalidation.
Monitoring classification
Success
Use payload and header checks to avoid false positives; cacheability depends on Cache-Control/ETag/Vary.

Related status codes

203 Non-Authoritative Information
The request was successful, but the enclosed payload has been modified by a transforming proxy from the origin server's 200 (OK) response.
205 Reset Content
The server successfully processed the request, asks that the requester reset its document view (e.g., clear a form for new input), and is not returning any content.
200 OK
The request has succeeded.
202 Accepted
The request has been accepted for processing, but processing is not complete. Often used for asynchronous operations or batch processing.
304 Not Modified
Indicates that the resource has not been modified since the version specified by the request headers (If-None-Match or If-Modified-Since). The client should use its cached copy.

Explore more

Related guides
Related tools
Related utilities