QA How-To
HTTP 204 no content: What Testers Need to Know
Master HTTP status 204 no content testing for bodies, headers, deletes, updates, caching, automation, edge cases, and practical API interview answers.
24 min read | 3,504 words
TL;DR
HTTP 204 No Content means the request was successfully fulfilled and the response has no content. Test the exact status, confirm that no response body is sent or parsed, validate allowed metadata headers, and prove the intended state change through a follow-up read or another authoritative oracle.
Key Takeaways
- A 204 response means the server successfully fulfilled the request and is not sending response content.
- Do not call a JSON parser on a 204 response, even if the endpoint normally returns JSON for other statuses.
- Verify the business state, because an empty successful response gives no representation to prove the mutation.
- 204 is common for DELETE and update operations, but no HTTP method automatically requires it.
- Response headers can still carry useful metadata such as ETag, cache controls, request IDs, and deprecation information.
- Repeated DELETE behavior can legitimately be 204 again or 404, depending on the documented contract and security model.
- Test framing and client compatibility so that stray bytes, misleading Content-Type, or incorrect Content-Length do not break connection reuse.
The http status 204 no content response communicates two facts: the server successfully fulfilled the request, and it is not sending response content. It is not a smaller 200 with optional JSON, and it is not proof that a DELETE occurred. The request method and endpoint contract define what successful fulfillment actually changed.
For QA engineers, 204 is deceptively rich. An empty response removes the easiest source of test evidence, so state and side-effect checks become more important. Protocol framing, client parsers, caching metadata, idempotent retries, and UI behavior can all fail even when the status assertion passes. This guide builds a complete 204 strategy with runnable Python tests.
TL;DR
| Test area | What a 204 contract should prove | Common defect |
|---|---|---|
| Status | Exact 204 for the documented branch | Broad 2xx assertion accepts 200 or 202 |
| Content | No response content and no JSON parsing | Server sends {} or client crashes on empty input |
| State | Intended update or deletion is durable | 204 returned before failed persistence |
| Headers | Useful metadata remains valid | Misleading Content-Type or invalid framing |
| Retries | Repeated operation follows policy | Duplicate side effects or unstable state |
| Security | Authorized mutation, no data leakage | Existence oracle or cached user-specific metadata |
A reliable test treats 204 as a response contract plus a state transition, not as an empty assertion.
1. What HTTP Status 204 No Content Means
204 No Content is a successful response status. The server has fulfilled the request, and there is no additional response content to send. The response ends after its header section. Headers can still describe the result or selected representation, depending on the method and endpoint. For example, a successful update can return an ETag for the new version without returning the full JSON representation.
The phrase no content matters at the protocol boundary. A server should not append a JSON object, an empty string payload, HTML, whitespace, or a diagnostic message after a 204 header section. Some frameworks suppress such data automatically. Others can produce contradictory headers or stray bytes through middleware. Different clients may ignore, reject, or misattribute those bytes to the next response on a reused connection.
204 does not mean no change. It frequently confirms a successful mutation where the caller does not need a representation. Nor does it mean the resource does not exist. A GET for a known resource normally returns 200 with content, while a missing resource normally returns 404. A 204 response to GET is possible but often a poor resource model because it says the request succeeded with nothing to represent.
The code is also distinct from 202. A 204 says fulfillment is complete. A 202 says processing has been accepted but may not be complete. If a DELETE queues work that can later fail, returning 204 can mislead the client unless the contract defines deletion at an accepted logical boundary. QA should identify the commit point behind the response.
2. Compare 204 with 200, 201, 202, 205, and 304
Neighboring codes are not cosmetic alternatives. They tell clients whether a representation exists, whether a resource was created, whether processing is complete, and whether existing cached content remains relevant.
| Status | Core meaning | Response content | Typical QA question |
|---|---|---|---|
| 200 OK | Request succeeded with a result | Usually present for representation-oriented operations | Does the body accurately represent the result? |
| 201 Created | Request succeeded and created resources | Optional representation, resource must be identifiable | What was created and where is it? |
| 202 Accepted | Request accepted, processing may be incomplete | Can describe status monitoring | Can the job later fail, and how is it tracked? |
| 204 No Content | Request fulfilled, no response content | None | Did state change, and can the client handle emptiness? |
| 205 Reset Content | Request fulfilled, client should reset its document view | No content | Does the client reset the relevant input view? |
| 304 Not Modified | Conditional retrieval can reuse a stored representation | No message content | Did validators and cache selection work correctly? |
A successful update can return 200 with the updated representation or 204 without it. A collection create typically returns 201 because creation identity matters. A long-running purge might return 202 with an operation URL. A form submission can use 205 when the user agent should reset the view, though 204 is far more common in APIs. A conditional GET can return 304, which participates in cache validation and is not a generic success substitute.
For a focused comparison of creation behavior, see HTTP 200 vs 201 for testers. The broader lesson is to assert the lifecycle meaning, not merely membership in the 2xx range.
3. Use 204 for DELETE, PUT, PATCH, and Actions Carefully
DELETE is the most familiar 204 case. If the server successfully removes the target resource or makes it no longer accessible under the contract, it can return 204 without a representation. The follow-up oracle might be 404, 410, an absent list item, or a tombstone state. The correct result depends on soft deletion, retention, legal holds, and authorization rules.
PUT and PATCH can also return 204 after a successful update when the client already knows the submitted values and does not need the full current representation. This saves response content, but it can hide server-generated changes such as normalized text, computed totals, updated timestamps, or workflow transitions. If clients need those values immediately, 200 with a representation may be a better contract. QA should test the contract chosen, not redesign it through assertions.
POST action endpoints sometimes use 204. POST /notifications/42/read can mark a notification as read and return no body. However, action endpoints need careful retry analysis. If repeating the request sends a message, increments a counter, or triggers billing, a 204 response does not make the action idempotent. The server may need an idempotency key or a state-based operation.
Do not hard-code DELETE -> 204. A DELETE can return 200 with a deletion status representation, 202 for asynchronous deletion, or another documented result. It can return 404 when the target is absent, 409 when current state prevents deletion, 412 when a precondition fails, 401 for missing authentication, or 403 for insufficient permission. A test matrix must cover these branches.
4. Test the Empty Response at the Protocol and Client Layers
A high-value 204 test verifies status, zero response bytes exposed by the client, and absence of content parsing. It also inspects headers for contradictions. Content-Type: application/json on a 204 can tempt generated clients to invoke a JSON decoder. A Content-Length value greater than zero is incompatible with the no-content outcome. Transfer framing and connection behavior deserve lower-level integration coverage when your gateway or middleware has produced framing defects.
At the application test level, read raw content and assert b''. Do not call response.json() and expect a special null value. Most JSON parsers correctly reject an empty document because an empty byte sequence is not valid JSON. Client code should branch on status or content length before deserialization. The test should mirror that safe client behavior.
Check that error responses from the same endpoint still carry their documented representation. A shared response helper might accidentally strip a 400 problem body because the success branch is 204. Conversely, exception middleware might append an HTML error after the framework has committed 204 headers. Exercise validation, authentication, authorization, conflict, and dependency branches.
If a reverse proxy compresses or transforms responses, verify it does not add Content-Encoding for a nonexistent body. Reuse one connection for a 204 request followed immediately by a normal 200 request. The second response must parse correctly. This catches stray-body corruption that a one-request client can miss. Component tests can inspect raw HTTP framing, while API tests should confirm behavior through the same client stack used by consumers.
5. Build Runnable pytest and HTTPX Checks
HTTPX exposes status, headers, and raw content without forcing JSON parsing. The following tests target an approved test API. Install dependencies with python -m pip install pytest httpx, set API_BASE_URL and API_TOKEN, then run pytest -q. The example uses an allowlist so an accidental production URL fails closed.
# test_profile_204.py
import os
from urllib.parse import urlparse
import httpx
import pytest
BASE_URL = os.environ.get("API_BASE_URL", "http://127.0.0.1:8080")
TOKEN = os.environ.get("API_TOKEN", "local-test-token")
ALLOWED_HOSTS = {"127.0.0.1", "localhost", "api.test.example"}
if urlparse(BASE_URL).hostname not in ALLOWED_HOSTS:
raise RuntimeError("Refusing to mutate an unapproved host")
@pytest.fixture
def client():
with httpx.Client(
base_url=BASE_URL,
headers={"Authorization": f"Bearer {TOKEN}"},
timeout=10.0,
) as api_client:
yield api_client
def test_update_preference_returns_a_true_204(client):
user_id = "qa-user-204"
response = client.patch(
f"/v1/users/{user_id}/preferences",
json={"weeklyDigest": False},
)
assert response.status_code == 204, response.text
assert response.content == b""
assert int(response.headers.get("content-length", "0")) == 0
read_back = client.get(f"/v1/users/{user_id}/preferences")
assert read_back.status_code == 200
assert read_back.json()["weeklyDigest"] is False
httpx.Client, patch, get, status_code, content, headers, and json are current public APIs. The example assumes a seeded test user. In a real suite, create isolated test data through a fixture and remove it through supported cleanup.
The content-length assertion tolerates a missing header by treating it as zero. The definitive application-level oracle is response.content == b''. Do not require a Content-Type header on the 204. If one appears, decide whether your platform standard forbids it and whether it breaks generated clients. Also avoid printing response headers wholesale because correlation or infrastructure headers can contain sensitive information.
6. Prove the Mutation Instead of Trusting the Status
Because 204 contains no representation, use an authoritative after-state check. For an update, GET the resource and verify both the changed field and important unchanged fields. For DELETE, retrieve the resource, list its collection, or query an audit endpoint according to the public contract. For a command, inspect the resulting state and controlled downstream ledger.
Capture the before state when necessary. If weeklyDigest was already false, a patch to false followed by a false read does not prove that the update path executed. Choose data that makes the transition visible, or assert version, update time, audit event, and repository interaction in appropriate test layers. Avoid brittle timestamp equality. Check a bounded interval or monotonic version rule instead.
A 204 should not be returned before a transaction that can still fail. Inject repository and downstream failures. If persistence fails, the API must produce the documented error rather than 204. If the response is lost after commit, retry behavior should converge safely. The empty response makes reconciliation especially important because the client has no result representation to store.
For deletion, confirm related behavior: access through aliases, collection membership, search indexes, child-resource policy, file storage, cache invalidation, and domain events. Not every check belongs in one end-to-end test. Divide them among component, API, integration, and scheduled consistency tests. Still, keep one traceable contract showing that the user-visible resource becomes unavailable as promised.
Pair the state checks with API contract testing with Pact when consumers rely on exact empty-response behavior. Contract tooling can prevent a provider from quietly adding JSON or changing 204 to 200.
7. Handle Repeated DELETE and Idempotency Semantics
DELETE has idempotent semantics: repeating the same request is intended to have the same effect on server state. That does not require identical status codes. The first request can return 204 and the second 404 because the resource remains absent after both. Another API can return 204 for both requests to avoid exposing prior existence or to model delete as ensuring absence. Both patterns can be coherent when documented.
Write the expected sequence explicitly. Create a unique resource, delete it, verify the first 204 and empty content, then repeat the deletion. Accept only the contractually specified result. In either case, prove no duplicate cleanup event, refund, notification, or billing action occurred. Idempotent state does not excuse duplicated side effects.
def test_repeated_delete_keeps_resource_absent(client):
created = client.post("/v1/labels", json={"name": "temporary-204-label"})
assert created.status_code == 201
label_id = created.json()["id"]
first = client.delete(f"/v1/labels/{label_id}")
assert first.status_code == 204
assert first.content == b""
second = client.delete(f"/v1/labels/{label_id}")
assert second.status_code == 404 # This example contract exposes absence.
read_back = client.get(f"/v1/labels/{label_id}")
assert read_back.status_code == 404
A race between two DELETE requests is another useful case. The service may return 204 and 404, or two 204 responses, but must finish with one consistent absent state. Soft-deleted resources require extra checks for restore permissions, list filters, uniqueness constraints, and retention jobs.
For systematic retry design, consult API idempotency testing. The key distinction is that method-level idempotency defines the intended repeated effect, while an idempotency key can correlate one logical action and prevent duplicate external consequences.
8. Validate Headers, Caching, ETags, and Preconditions
No content does not mean no metadata. A 204 can carry request IDs, deprecation notices, rate-limit information, ETag, Last-Modified, or cache directives where applicable. After a PUT or PATCH, an ETag can describe the new selected representation. The client can use it in a later If-Match request to prevent lost updates. Test that the tag changes when representation state changes and remains compatible with the server's strong or weak validation policy.
Conditional mutation creates an important branch. Send If-Match with the current ETag and expect the successful documented response, possibly 204 plus a new ETag. Send a stale tag and expect 412 Precondition Failed with no mutation. Two concurrent clients should not both overwrite each other silently when the endpoint promises optimistic concurrency.
204 responses are cacheable by default under HTTP rules unless method definitions or explicit cache controls say otherwise, but most mutation responses should not be stored by shared caches casually. Verify organizational policy for sensitive and user-specific endpoints. A response with no body can still leak existence, version, quota, or timing metadata if cached across users. Include Cache-Control assertions when the risk and contract justify them.
CORS headers also remain relevant. A browser client must be allowed to call the mutation, and if it needs to read a custom version or request header, Access-Control-Expose-Headers must include it. Preflight behavior is separate from the final 204. Some servers also answer an OPTIONS preflight with 204. Test allowed origins, methods, and headers without confusing preflight success with authorization of the actual business request.
9. Test Client, UI, Gateway, and Observability Behavior
Generated clients sometimes assume that every 2xx response for an endpoint has the same schema. A 204 branch can then call a decoder and throw an end-of-input error after the mutation actually succeeded. Add a consumer test that invokes the generated SDK, not only raw HTTPX. It should return a documented void, unit, or optional result without treating emptiness as failure.
UI behavior needs an explicit oracle. After a 204 update, the interface can apply an optimistic local change, refetch the resource, show a success message, or navigate away. Ensure it does not wait for a JSON body forever, display an error after success, or retain stale values. On a 204 DELETE, remove the item or refetch the collection, then handle back navigation and deep links consistently.
Gateways and middleware can rewrite statuses. A function might return 204, while an API gateway wraps it in 200 with {}. Compression middleware may add misleading headers. Observability middleware may attempt to sample response content and fail. Test at both the service boundary and the deployed edge to locate transformations.
Log enough to reconcile success without logging sensitive headers. Record endpoint template, exact status, safe request ID, authorized actor identifier according to policy, resource reference when allowed, and mutation outcome. Metrics can track 204 rate and latency. Avoid raw resource IDs as high-cardinality labels. A sharp drop in 204 or rise in client-side JSON errors after a release can reveal a contract or SDK regression.
Browser-facing API checks fit well beside Playwright API testing techniques, especially when the same authenticated context drives a mutation and a UI verification.
10. Create the HTTP Status 204 No Content Test Matrix
Build a matrix per endpoint rather than copying a single happy-path check. A strong baseline includes:
- Valid authorized mutation -> exact 204, zero content, correct state.
- Valid update with server normalization -> verify final state through GET or ETag.
- Missing or malformed input -> 400 or 422 with the documented error body.
- Missing authentication -> 401, no state change.
- Insufficient permission -> 403, no state change and no leaked resource details.
- Missing target -> documented 404 or idempotent 204 policy.
- Current-state conflict -> 409, unchanged resource.
- Stale precondition -> 412, unchanged resource.
- Unsupported media type -> 415 before mutation.
- Dependency or persistence failure -> failure response, no false 204.
- Repeated and concurrent request -> stable state, no duplicate side effects.
- 204 followed by a 200 on one connection -> both responses parse correctly.
Add representation preference when supported. Some APIs honor Prefer: return=representation and answer 200 with the updated resource, while Prefer: return=minimal leads to 204. If the contract supports these preferences, test Preference-Applied, response schemas, and identical final state. Do not assume support merely because the header exists.
Run fast status, content, and state cases on every change. Run proxy framing, concurrent request, SDK, browser, and fault-injection cases at the layer where they are reliable. Diagnose failures with sanitized raw lengths and headers, but never paste tokens or personal data into reports.
11. Review Accessibility and User Feedback for Silent Success
A 204 response is silent at the HTTP content layer, but the product should not be silent to a user who needs confirmation. A UI that saves settings, deletes a record, or marks an item complete must expose a visible and accessible result. Test focus management, live-region announcements, disabled-button recovery, loading indicators, and error rollback. The API code does not choose the UX, but it affects how the client detects completion.
For optimistic updates, slow the response and verify the interim state. Then return 204 and confirm the optimistic change settles without a parsing exception. Return 409 or 500 and confirm the UI restores or refetches accurate state. For deletion, ensure keyboard and screen-reader users receive context, focus moves to a sensible item, and undo behavior, if offered, maps to a real supported operation.
A frontend may use fetch and correctly avoid parsing:
export async function deleteLabel(id: string, token: string): Promise<void> {
const response = await fetch(`/api/labels/${encodeURIComponent(id)}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${token}` },
});
if (response.status !== 204) {
const detail = await response.text();
throw new Error(`Delete failed with ${response.status}: ${detail}`);
}
}
This uses the standard Fetch API and intentionally does not invoke response.json() on success. Production code should avoid exposing sensitive server detail to end users and should map known problem responses to useful messages. A UI test can stub the backend at 204 and prove that the caller resolves to undefined while the interface announces success.
Interview Questions and Answers
Q: What does HTTP 204 No Content mean?
It means the server successfully fulfilled the request and is not sending response content. Headers can still carry metadata. I verify both the empty response and the resulting business state.
Q: Can a 204 response contain JSON?
No response content should follow a 204 header section. Sending {}, whitespace, or another payload contradicts the status and can break clients or connection framing. If JSON is required, a 200 response is usually the clearer contract.
Q: Is 204 the only correct status for DELETE?
No. DELETE can return 204, 200 with a representation, or 202 for deferred processing, plus relevant error codes. The endpoint contract and completion model determine the exact expectation.
Q: Should a test call response.json() for a 204?
No. An empty byte sequence is not a JSON document, so parsing can raise an error. The client should branch on 204, verify or return a void result, and parse bodies only for statuses that define one.
Q: What should a repeated DELETE return?
It can return 204 again or return 404 after the first successful delete, depending on the documented policy. DELETE remains idempotent if the repeated effect keeps the resource absent. Tests should also verify that external side effects are not duplicated.
Q: What is the difference between 204 and 202?
204 communicates that the request was fulfilled. 202 says the request was accepted but processing may still be incomplete. For asynchronous deletion, I expect an operation model rather than a premature claim of completion.
Q: Can a 204 response have an ETag?
Yes. Headers can describe the selected representation after a successful mutation even when no representation is returned. I test that the ETag matches the resulting version and supports the documented conditional-request workflow.
Q: How do you prove a 204 update succeeded?
I read the resource from an authoritative interface, validate changed and protected fields, and inspect required side effects. I also inject persistence failure to confirm the API does not return 204 before the operation is safely complete.
Common Mistakes
- Calling a JSON parser on every 2xx response, including 204.
- Returning
{}or whitespace with 204 and assuming clients will ignore it. - Checking only the status without reading back authoritative state.
- Assuming every DELETE must return 204 or that every repeated DELETE must return the same code.
- Confusing completed 204 behavior with asynchronous 202 acceptance.
- Requiring
Content-Type: application/jsonon a response that has no content. - Ignoring ETag, cache controls, CORS exposure, request IDs, and other useful headers.
- Allowing proxies or middleware to rewrite 204 into a misleading 200 wrapper.
- Skipping connection-reuse and generated-client tests for empty-response framing.
- Showing no accessible UI confirmation because the API response has no body.
Conclusion
The http status 204 no content contract is precise: successful fulfillment, no response content. Its emptiness increases the need for strong state, side-effect, header, framing, client, and UX checks. A passing status assertion alone cannot show that the mutation committed or that consumers handled it safely.
Choose one 204 endpoint and add three assertions now: raw content is empty, the intended state is observable, and a real client does not attempt JSON deserialization. Then add retry and failure cases. Those checks turn a deceptively simple response into a trustworthy API contract.
Interview Questions and Answers
How would you define HTTP 204 in an interview?
204 No Content means the server fulfilled the request successfully and sends no response content. Headers can still describe metadata. My test verifies empty content plus the actual state transition.
Why can `response.json()` fail on 204?
A 204 response has no content, and an empty sequence is not a valid JSON value. A correct client branches before decoding and models the result as void or optional. Tests should read raw content when validating emptiness.
How do 200 and 204 differ for an update?
Both can report successful completion. A 200 response returns a representation of the result, while 204 returns no content. The API contract should choose based on whether the client needs that representation.
Can DELETE return 204 twice?
Yes, if the contract models deletion as ensuring absence. It can also return 204 then 404 while remaining idempotent. I enforce the documented policy and verify no duplicate business side effects.
What headers would you inspect on a 204?
I inspect invalid content framing first, then contract metadata such as ETag, Cache-Control, request ID, CORS exposure, and deprecation headers. I do not require Content-Type for nonexistent content. Sensitive metadata must not leak across users.
How do you test 204 through a gateway?
I compare the service boundary with the deployed edge, checking exact status, raw content length, and relevant headers. I reuse a connection for a following 200 response and exercise the generated SDK. This detects wrapping, compression, and stray-byte defects.
What is the 204 versus 202 distinction?
204 says the request has been fulfilled, while 202 says it has only been accepted for possible later processing. If deletion can still fail asynchronously, I expect an operation resource and lifecycle rather than false completion. A follow-up state check proves which completion model the service implements.
How would you test a UI backed by a 204 API?
I verify that the UI does not parse JSON, updates or refetches state, and gives accessible success feedback. I also return error statuses to confirm optimistic changes roll back. Loading and focus behavior must settle correctly after the empty response.
Frequently Asked Questions
What does HTTP 204 No Content mean?
It means the server successfully fulfilled the request and sends no response content. Response headers can still provide metadata. The request method and endpoint contract determine what business state was changed.
Can HTTP 204 return a body?
No response content should be sent with 204. JSON, whitespace, or diagnostic text can contradict the status and cause parsing or connection-framing problems. Use a status that permits content when a representation is needed.
Should I parse JSON from a 204 response?
No. Empty content is not valid JSON, and parsers commonly raise an end-of-input error. Branch on the status and return a void or optional result without parsing the successful 204 response.
Is 204 the correct status for every DELETE request?
No. A completed delete can return 204 or 200 with a representation, while deferred processing can return 202. Missing, forbidden, conflicting, or failed deletions need their documented error statuses.
Can a repeated DELETE return 404 after 204?
Yes. Idempotency requires the repeated request to preserve the intended absent state, not to return an identical response. Some contracts use 204 again, while others expose absence with 404.
Can HTTP 204 include headers?
Yes. Headers such as ETag, Cache-Control, request identifiers, deprecation notices, and CORS metadata can still be meaningful. They must not imply nonexistent response content or leak information.
How do testers verify a 204 update?
Assert the exact status and empty raw content, then retrieve or inspect authoritative state. Verify protected fields and downstream effects, and inject failures to ensure the service does not claim success prematurely.