HTTP Expect: 100-continue: The Rules for Waiting Before Sending a Request Body

How Expect: 100-continue works under RFC 9110 and RFC 9112: rules for clients, origin servers, and proxies, what 417 Expectation Failed means, and closing the connection after an early rejection.

httpexpect100-continuerfc9110http-1.1

If you PUT a multi-gigabyte file and only get a 401 (Unauthorized) or 405 (Method Not Allowed) after the whole body has gone out, all of that data was wasted. Expect: 100-continue exists to cut that waste. The client sends the headers first and can wait for the server’s 100 Continue before sending the body.

This article relies only on RFC 9110 (June 2022), the current HTTP semantics standard, and RFC 9112 (June 2022), which defines HTTP/1.1 messaging and connection rules. It does not cover how long any particular client library or server product actually waits, or what its defaults are. The standard leaves those choices to implementations, so check them in each project’s official documentation.

The request flow

The example request in RFC 9110 Section 10.1.1 begins like this:

PUT /somewhere/fun HTTP/1.1
Host: origin.example.com
Content-Type: video/h264
Content-Length: 1234567890987
Expect: 100-continue

The RFC explains that this lets the origin server respond right away with an error such as 401 or 405, before the client starts pushing unnecessary data. If the server is willing to accept the body, it sends a 100 (Continue) interim response. The client discards that response and keeps sending the body. Once the server has received and processed the body, it sends the final response.

So there are two paths:

  1. The headers alone decide the outcome: the server immediately sends a final status code (for example, 401 or 405).
  2. The body is needed: the server immediately sends 100 Continue, then sends a final response after receiving the body.

RFC 9110 names two cases where this can improve efficiency: when the data is huge, and when the client expects an error. Its example is sending a state-changing method for the first time without previously verified authentication credentials.

The Expect field itself

  • The Expect field value is case-insensitive.
  • 100-continue is the only expectation RFC 9110 defines, and it has no defined parameters.
  • The grammar is a list (#expectation). RFC 9110 Appendix B.3 (changes from RFC 7231) says the list-based grammar was restored for compatibility with RFC 2616.
  • If the value contains anything other than 100-continue, the server MAY respond with 417 (Expectation Failed).

Client requirements

These are the client requirements from RFC 9110 Section 10.1.1:

  • A client MUST NOT send a 100-continue expectation in a request that has no content.
  • A client that will wait for 100 Continue before sending the content MUST send Expect: 100-continue.
  • Sending 100-continue does not oblige the client to wait any specific length of time. It MAY start sending the content without having received a response.
  • Because HTTP/1.0 intermediaries cannot forward 100 Continue, the client SHOULD NOT wait indefinitely.
  • A client that gets a 417 for a request carrying 100-continue SHOULD repeat the request without the expectation. The RFC explains that 417 only means something in the response chain (for example, an HTTP/1.0 server) does not support expectations.

The key point is that the standard gives no number for the wait. How long to wait before sending anyway is up to the implementation, which is why clients can behave differently.

RFC 9112 Section 9.5 also covers what to do while the body is being sent. A client sending a message body SHOULD watch the connection for an error response. If it sees a response saying the server does not want the body and is closing the connection, it SHOULD stop sending right away and close its side of the connection.

Origin server requirements

Consider an HTTP/1.1 (or later) request whose method, target URI, and complete header section have arrived, with a 100-continue expectation and an indication that content will follow. The origin server MUST send one of these:

  • an immediate final status code, if the method, target URI, and headers are enough to decide it, or
  • an immediate 100 Continue otherwise.

The origin server MUST NOT wait for the content before sending 100 Continue. As I read it, this rule prevents the case where the client waits for a 100 while the server waits for the body, and neither moves.

The other server requirements:

  • A server MUST ignore a 100-continue expectation in an HTTP/1.0 request. It also MUST NOT send any 1xx response to an HTTP/1.0 client (RFC 9110 Section 15.2).
  • A server MAY skip 100 Continue if it has already received some or all of the content, or if the message framing shows there is no content.
  • A server that sends 100 Continue MUST eventually send a final status code after it receives and processes the content, unless the connection closes early.
  • A server that sends a final status code before reading the whole body SHOULD indicate whether it will close the connection or keep reading the body.

Handling the connection after an early rejection

That last item needs RFC 9112 next to it. Under RFC 9112 Section 9.3, a server MUST either read the entire request body or close the connection after sending its response. Otherwise, leftover body data on a persistent connection would be misread as the next request.

The way to signal an intended close is Connection: close (RFC 9112 Section 9.6). But if the server drops the TCP connection immediately, data the client is still sending can make the server’s TCP stack send a reset, and that reset can wipe out a response the client has not read yet. RFC 9112 therefore describes how servers typically close in stages: first close only the write side (a half-close), keep reading until the client closes or the server is reasonably sure its last response arrived, and then close fully.

Proxy requirements

A proxy that receives the same kind of request MUST do one of these:

  • send an immediate final status code, if the method, target URI, and headers are enough to decide it, or
  • forward the request line and header section to the next inbound server.

If the proxy believes, from configuration or past interaction, that the next inbound server only supports HTTP/1.0, it MAY generate a 100 Continue itself.

A proxy MUST forward 1xx responses, with one exception: when the proxy itself asked for the 1xx. For example, if a proxy adds Expect: 100-continue while forwarding a request, it need not forward the matching 100 Continue (RFC 9110 Section 15.2).

The definition of 417 fits this structure. RFC 9110 Section 15.5.18 defines 417 as meaning that the expectation in Expect could not be met by at least one of the inbound servers. In other words, whoever sent the 417 might not be the origin server.

Things to know about 1xx responses

  • A 1xx response ends at the end of its header section. It cannot have content or trailers (RFC 9110 Section 15.2, RFC 9112 Section 6.3).
  • A client MUST be able to parse one or more 1xx responses that arrive before the final response, even if it did not expect them. A user agent MAY ignore unexpected 1xx responses.
  • A 100 Continue received without having sent Expect: 100-continue can simply be discarded (RFC 9110 Section 15.2.1).
  • HTTP/1.1 has no request identifier, so a client MUST match each received response to the first outstanding request that has not yet received a final (non-1xx) response (RFC 9112 Section 9.2). If a client treats a 100 as the final response, later responses on the same connection can end up matched to the wrong request.
  • When a request carries both Upgrade and Expect: 100-continue, the server MUST send 100 Continue before 101 (Switching Protocols) (RFC 9110 Section 7.8).

Checklist

These are my suggestions drawn from the rules above. They are not RFC wording.

  • Clients: put an upper bound on the wait for 100 Continue, and confirm the client sends the body once that time passes. Also confirm there is a path that retries once without Expect after a 417.
  • Clients: confirm requests without a body (such as a GET with no body) do not carry Expect.
  • Origin servers: run checks that only need the headers, such as authentication, authorization, whether the method is allowed, and size limits, before reading the body. That is what makes the required “immediate final response or immediate 100” possible.
  • Origin servers: if you reject before reading the body, either send Connection: close and close in stages, or read and discard the rest of the body. If you do neither, the leftover data can be parsed as the next request.
  • Proxies and gateways: check that 1xx responses are forwarded, and if the proxy adds Expect itself, check how it handles the resulting 100.

Scope and limits

  • This article covers only the rules in RFC 9110 and RFC 9112. RFC 9110 states its requirements for “HTTP/1.1 (or later)” requests, but how messages are framed in HTTP/2 and HTTP/3 is defined in their own RFCs, which this article did not check.
  • Default wait times and whether Expect is added automatically vary by HTTP client, server, and proxy. This article does not cover them.

References

  • RFC 9110, HTTP Semantics (STD 97, June 2022): Section 7.8 Upgrade, Section 10.1.1 Expect, Section 15.2 Informational 1xx, Section 15.2.1 100 Continue, Section 15.5.18 417 Expectation Failed, Appendix B.3. https://www.rfc-editor.org/rfc/rfc9110.txt
  • RFC 9112, HTTP/1.1 (STD 99, June 2022): Section 6.3 Message Body Length, Section 9.2 Associating a Response to a Request, Section 9.3 Persistence, Section 9.5 Failures and Timeouts, Section 9.6 Tear-down. https://www.rfc-editor.org/rfc/rfc9112.txt

Back to the blog