100 Continue
Informational response defined by RFC 9110 §15.2.1.
Last updated July 27, 2026
Common causes at a glance
- The client library added Expect: 100-continue automatically for a large request body
- curl adds the header for PUT/POST bodies larger than 1 KB unless told not to
- An HTTP/1.0 proxy or load balancer in the path does not understand the expectation
- A server or WAF that answers 100 before running auth checks, defeating the point
Reproduce it
curl -v -X POST --data-binary @big.json -H 'Expect: 100-continue' https://api.example.com/v1/importWhat 100 Continue means
100 exists so a client with a large body can find out whether the server
will reject the request before spending bandwidth uploading it. The client sends
headers only, with Expect: 100-continue, and waits. If the server likes
what it sees it replies 100 Continue and the client streams the body. If
not, it replies with a final status such as 401 or 413 and the client never sends the
payload at all.
It is a genuinely useful optimisation that almost nobody configures deliberately, because HTTP libraries turn it on and off on your behalf.
What the spec says
RFC 9110 §15.2.1 defines the code, and
§10.1.1 defines the Expect header field that
triggers it. A server that understands the expectation must either send 100 or send a
final error status. Importantly, a client that sends the expectation must not wait
forever: the spec says it should send the body anyway after a short timeout, because
an intermediary may have swallowed the expectation. Interim 1xx responses are
forwarded by proxies but are invisible to most application code.
What actually causes it
Seeing 100 in a trace is normal, not an error. What you actually
notice in production is the latency it adds when something in the path mishandles it.
curl adds Expect: 100-continue by itself once a PUT or
POST body exceeds roughly a kilobyte. If the peer never answers the
expectation, curl waits — historically one second — and then sends the body anyway.
A fleet of uploads each paying a fixed extra second is the classic symptom, and it
usually points at an old proxy, an HTTP/1.0 hop, or a middlebox that strips the
header on the way in but not the response on the way out.
How to debug it
Run the request under curl -v and look for the interim status
line. You will see < HTTP/1.1 100 Continue followed later by the real
status. If the 100 never appears and there is a visible stall before the body goes
out, the expectation is being dropped somewhere.
The cheapest fix is to stop asking. Passing an empty header removes it:
curl -v -X POST --data-binary @big.json -H 'Expect:' https://api.example.com/v1/import
Python’s requests never sends the expectation, so if you are comparing a
curl reproduction against application traffic that is one difference to rule out
early. On the server side, nginx does not support sending 100 from a proxied upstream
in every configuration, so check the
proxy module docs before
concluding the application is at fault.
Working examples
curl
# curl adds Expect: 100-continue on its own for bodies over ~1 KB.
# -v shows the interim response; -H 'Expect:' suppresses the whole dance.
curl -v -X POST \
-H 'Content-Type: application/json' \
--data-binary @big.json \
https://api.example.com/v1/import
Python (requests)
import requests
# requests does not send Expect: 100-continue, so you have to opt in
# and read the interim response through the underlying urllib3 connection.
resp = requests.post(
"https://api.example.com/v1/import",
headers={"Expect": "100-continue", "Content-Type": "application/json"},
data=open("big.json", "rb"),
timeout=30,
)
print(resp.status_code) # the FINAL status, never 100
Node (fetch)
// fetch() hides interim responses entirely. Use node:http to observe them.
import http from "node:http";
const req = http.request(
{ host: "api.example.com", path: "/v1/import", method: "POST",
headers: { Expect: "100-continue", "Content-Type": "application/json" } },
(res) => console.log("final:", res.statusCode),
);
req.on("continue", () => {
console.log("got 100 Continue, sending body");
req.end(JSON.stringify({ records: [] }));
});
req.on("response", (res) => console.log("server answered early:", res.statusCode));Reference and tooling
The authoritative list of assigned status codes is theIANA HTTP Status Code Registry. For a searchable copy with the semantics summarised, use theHTTP status code reference on this site. To see exactly what a server is sending, theheader inspector shows the raw status line and response headers, and thecurl builder assembles the request flags for you.