An HTTP/1.1 client can send request headers containing Expect: 100-continue and hold back the request body while the server evaluates those headers. The server can answer with 100 Continue, allowing body transfer to proceed, or send a final response when it can reject the request without receiving the payload. This splits one request into a metadata decision boundary followed, conditionally, by body transmission.
The mechanism matters most when a request body is costly to transmit and the server can make a useful decision from request metadata alone. Authentication failure, an unsupported method, or another header-visible rejection can terminate the exchange before those bytes cross the connection. The same split also creates a timing dependency: a client cannot wait forever for an interim response, and servers and intermediaries must preserve the protocol semantics closely enough for progress.
The expectation changes transmission order, not request meaning
Expect: 100-continue does not create a second application request. The method, target, headers, and eventual content still form one request. The expectation changes when the client transmits the content relative to the server’s initial response.
A simplified exchange looks like this:
Client Server
| POST /objects HTTP/1.1 |
| Content-Length: 8000000 |
| Expect: 100-continue |
|------------------------------------------>|
| |
| HTTP/1.1 100 Continue |
|<------------------------------------------|
| |
| [request content] |
|------------------------------------------>|
| |
| HTTP/1.1 201 Created |
|<------------------------------------------|The 100 response is informational. It is not the final status for the request, so the client continues processing until a final response arrives. If the server instead sends a final status such as 401 Unauthorized after inspecting the headers, the client can avoid sending the body for that exchange.
That distinction makes the expectation a transport-efficiency mechanism rather than an authorization or validation mechanism. A server that sends 100 Continue has not promised that the request will succeed. It has only indicated that, based on the information available at that point, the client should continue sending content.
Header-visible rejection defines the useful boundary
The optimization only saves body transfer when the server can reject the request before reading that body. A decision that depends on a checksum stored inside the payload, a field in a JSON document, or parsing the uploaded object necessarily lies after content begins arriving.
This creates a concrete architectural boundary. Conditions placed in headers can sometimes participate in an early decision; conditions encoded only in the body cannot. Moving arbitrary application data into headers merely to exploit this boundary is not automatically sound, since header fields have their own semantics, size constraints, caching interactions, and security considerations.
The server also needs no special reason to delay a final rejection until after 100 Continue if it already has enough information to reject. Sending the interim response first would remove the bandwidth-saving property for that case because the client is then permitted to start transmitting content.
Conversely, receipt of Expect: 100-continue does not require a server to pre-validate every condition that could later fail. Application state can change, body parsing can fail, and downstream processing can reject the operation after transfer starts. The interim response therefore marks permission to proceed, not a transaction commit point.
Waiting introduces a latency trade
Holding the body until an interim response arrives adds a round trip when the server promptly emits 100 Continue. For small payloads, the saved transfer on rejection may be worth less than the added delay on accepted requests.
Clients therefore commonly need a bounded waiting policy. HTTP semantics permit a client that sent the expectation to proceed with content without waiting indefinitely, because HTTP/1.0 intermediaries cannot be relied upon to forward a 100 Continue response. The exact timeout policy is an implementation choice rather than a protocol-wide fixed duration.
This produces three observable timing paths:
prompt 100 response -> wait, then send body
prompt final response -> wait, then omit body
no timely response -> client may send body after its wait policyThe third path is essential for liveness. If a client required the interim response forever while some intermediary suppressed it, the request could stall permanently: the client would wait for permission while the server might wait for content.
As a result, Expect: 100-continue is not a guarantee that rejected requests transfer zero body bytes. A client may begin transmission after its waiting period, and a final rejection can race with content already entering the connection.
A final response can cross content already in flight
Network exchanges are not lockstep message diagrams. Once the client starts sending the body, bytes can be buffered in the client, kernel, network, intermediary, or server-side stack while a final response travels in the opposite direction.
A server can therefore decide to reject a request after some content has arrived even though the client has not yet finished sending it. The client may observe the final response only after additional body bytes have already left its process.
This limits claims about bandwidth savings. The expectation creates an opportunity for early rejection before content transfer, but exact bytes avoided depend on response timing, buffering, connection behavior, and client implementation.
The distinction also matters for server code. Rejecting a request and deciding what to do with unread request content are separate concerns. Connection reuse can depend on protocol version, message framing, server behavior, and whether the remaining request bytes can be safely delimited or discarded. An application handler should not infer a universal connection-reuse outcome solely from its chosen status code.
Intermediaries participate in the expectation path
HTTP deployments often place proxies, gateways, load balancers, or other intermediaries between client and origin. Informational responses have to traverse that path according to the applicable HTTP semantics.
An intermediary can also be the component that determines a final response. For example, a gateway with sufficient request metadata might reject a request before forwarding content to the origin. In that case the useful early-decision boundary exists at the gateway rather than at the application server.
The protocol accounts for compatibility with older HTTP hops. A client cannot assume that every path will relay an informational response exactly as a direct modern HTTP connection would. This compatibility constraint is one reason bounded client waiting is part of the mechanism’s practical shape.
Observability can become misleading when tooling records only final responses. A trace showing a 401 and no explicit 100 event does not by itself establish whether the client waited, whether an intermediary generated an informational response, or how much request content had already moved. Packet-level or sufficiently detailed protocol telemetry is required for those claims.
HTTP/2 and HTTP/3 keep the informational status but change transport mechanics
The 100 Continue status is part of HTTP semantics, not an HTTP/1.1-only application concept. HTTP/2 and HTTP/3 carry informational responses using their own framing rather than the textual HTTP/1.1 message format.
The transport consequences differ because HTTP/2 and HTTP/3 multiplex streams. HTTP/1.1 exchanges on one connection are constrained by its message ordering and framing model, while later versions represent request and response fields and content within stream-oriented protocol machinery.
The semantic boundary remains recognizable: request fields can arrive before request content, and a 100 informational response can indicate that content should follow. Yet implementation analysis has to use the framing and stream rules of the actual HTTP version. Reasoning from raw HTTP/1.1 byte sequences does not directly describe HTTP/2 frames or HTTP/3 frames.
This is a broader protocol design point: preserving an HTTP semantic across versions does not imply preserving its wire representation or all of its transport-side consequences.
Early rejection does not make non-idempotent retries safe
A client can face ambiguous outcomes around any network request. Expect: 100-continue narrows one transmission pattern but does not establish whether an application operation took effect after failures later in the exchange.
Suppose the server sends 100 Continue, receives the body, commits an operation, and the final response is lost. The expectation provides no evidence that retrying the request is safe. Retry behavior still depends on method semantics, application idempotency, idempotency keys or equivalent mechanisms, and the failure point that can actually be established.
Even a failure before body transmission needs careful interpretation when intermediaries are involved. The absence of a body does not turn every method into an idempotent operation; it only constrains what this client sent on this connection. Application effects depend on what components received and processed.
The expectation should therefore remain separate from retry policy. One controls conditional body transmission. The other controls whether repeating an operation can duplicate or alter effects.
The mechanism is valuable only when its boundary matches the workload
Large uploads with meaningful header-only rejection conditions are the clearest fit. The potential avoided transfer is substantial, and the server can make a decision before payload processing. Small requests with high acceptance rates can see mostly the extra waiting path, especially when client and server implementations use the mechanism conservatively.
That does not produce a universal payload-size threshold. Network latency, transfer rate, rejection frequency, intermediary behavior, and client timeout policy all affect the trade. Any numeric cutoff belongs to a measured deployment or a documented client implementation, not to HTTP semantics itself.
The stable property is narrower. Expect: 100-continue creates a conditional transmission boundary between request fields and request content. It can prevent unnecessary body transfer when a final decision is available early, while bounded waiting preserves progress when the informational response does not arrive. Its benefit and its failure modes both come from that same split.