Technology · PRACTICAL GUIDE
curl says Empty reply after 100 or 103? Check the final response
Received headers do not always mean a complete response. Six controlled curl 8.5.0 cases separate informational replies, valid empty bodies and error 52.

An HTTP/1.1 trace can contain 100 Continue or 103 Early Hints and still end with curl: (52) Empty reply from server. In our controlled curl 8.5.0 test, 103-only produced 71 saved header bytes and response_code 103 before exit 52. A nonempty header file, or a response code other than 000, was not proof that a final response arrived.
The practical test is whether a final response follows the informational block and whether the transfer and expected application result complete. This guide adds that specific exception to existing empty-response explanations; it does not identify which component broke your connection.
Keep the command exit separate from HTTP status
Preserve curl --version, the exact method and URL, environment, proxy choice, error text and timestamps. For an authorized harmless request, save headers and body separately. An example is curl -sS -D response.headers -o response.body -w 'code=%{response_code} headers=%{size_header} body=%{size_download}\n' 'https://example.com/'; immediately save the shell exit status, such as rc=$? in a POSIX shell. Substitute the intended endpoint and retain necessary authentication privately.
Do not pipe the body into another program for this first comparison or infer curl's exit from the last pipeline command. The write-out HTTP code and process exit describe different things. This diagnostic command does not add --fail: a completed HTTP 4xx/5xx response can therefore have curl exit 0 and still be an application failure. Inspect the actual final status and expected content.
Read every response block in order
RFC 9110 distinguishes interim 1xx responses from a final response; RFC 8297 says 103 header hints do not replace final-response headers. Look through the saved blocks rather than stopping at the first HTTP status line. For this issue, 100 or 103 followed by connection closure is different from 103 followed by a final 200.
A proxy CONNECT response and redirect history can also create multiple blocks, so keep the method, peer and sequence attached to the trace. Do not label every earlier block an informational reply. A 100 Continue concerns request progress; it is not proof an upload's application action completed. A 103 Link header offers a hint, not proof that the hinted resource or main request succeeded.
Compare six known-byte controls
We used Python 3.12.14 to serve six synthetic responses on 127.0.0.1, draining complete GET request headers before an orderly close. The client was Ubuntu curl 8.5.0-2ubuntu10.13 with --http1.1, --noproxy '*', a two-second connect limit and three-second total limit. Those proxy settings isolate this local fixture; they are not instructions to bypass a required production proxy. No TLS, application server or intermediary was tested.
No response bytes gave code 000, zero header/body bytes and exit 52. Final 204 gave code 204, 46 header bytes, no body and exit 0; final 200 with Content-Length: 0 gave code 200, 57 header bytes, no body and exit 0. A 100-only block gave code 100, 25 header bytes and exit 52; 103-only gave code 103, 71 header bytes and exit 52. Both had no body. Adding a final 200 with Content-Length: 2 and body OK after that 103 gave code 200, 128 header bytes, two body bytes and exit 0. The 100-only case is intentionally incomplete parser input, not a normal complete response. Exact byte counts depend on these chosen headers.
Find where the final response stopped reaching the client
An OpenLiteSpeed community trace already shows the relevant puzzle: the client received 100 then error 52 while an upstream log recorded 200. That historical report establishes a real question, not a confirmed current product bug. Our loopback fixtures reproduce only the client-visible response shapes, not that deployment or its cause.
On systems you control, correlate one safe request across client, proxy and origin using timestamps and a request identifier. Ask which boundary logged an actual final response and which boundary delivered it downstream. An origin's 200 log alone does not prove curl received those bytes. Preserve the actual build: a separate 2026 curl-library exchange discusses 52 versus 56 for another reproducer, so do not promise identical error codes across versions or failure modes.
Accept a repair by the final result
After one narrow, reversible repair, repeat the intended safe request on its real path. Require the expected final status, a completed curl transfer and the expected body or legitimate absence of one. A 204 legitimately has no response content; a 200 with explicit zero-length framing can also be valid. Neither is the same as receiving only an informational block.
For an upload or state-changing API, establish whether the action already happened before retrying. Error 52 does not prove it did not. Keep authentication and certificate verification intact, and do not manufacture success by suppressing stderr, increasing unrelated timeouts or accepting 100/103 as completion. If ownership is unclear, hand off the redacted ordered blocks, exit code, build and correlated logs.
