skip to content

For a service accepting multi-gigabyte uploads, would you rely on the HTTP Expect: 100-continue mechanism to reject bad requests before the body is transferred? What would you weigh, and what alternatives exist?

level: principalimportance: nice to knowfreq 20%

answer

  1. Optimization with no floor
  2. Only header-decidable rejections pay
  3. Proxies erase the benefit silently
  4. Pre-flight + pre-signed target
  5. Hard body limits are the real guarantee

basics

~20 s

Use it as an optimization, never as the guarantee. It only helps when every hop honours it, and it saves nothing for checks that need the body. Pair it with server-side limits, and prefer pre-authorized or resumable upload flows for the real design.

solid answer

~60 s

I would enable it, but I would not build the design on it. It is opportunistic: it saves a wasted transfer when the path honours it, and degrades to a fixed one-second delay or a full upload when it does not. Proxies that strip `Expect`, swallow 1xx, or answer `100 Continue` themselves all erase the benefit silently, so I cannot promise 'we reject before the body' to anyone. What I would weigh: how often rejection is decidable from headers alone (auth, declared size, media type, quota) versus needing the body (checksum, schema, scan); the client population and whether they are library clients I control; the network path and how many hops there are; and the cost of the wasted transfer against one extra round trip per request. The structural alternatives are better: a pre-flight that authorizes and sizes the upload and returns a short-lived pre-signed target; resumable or chunked uploads so a rejection costs one chunk; and, on HTTP/2 or /3, cancelling the stream mid-upload. Plus hard server-side body limits that abort regardless.

go deeper

for a junior

Say it can help avoid sending a body that would be rejected, but the server still needs its own size limits.

for a middle

Weigh the extra round trip against the expected saving and note that only header-decidable rejections benefit.

for a senior

Enumerate the proxy failure modes that erase the benefit and pair the mechanism with hard byte-counting limits and connection aborts.

for a principal

Take a position: opportunistic optimization on paths you control, with the real guarantees carried by a pre-flight authorization step, resumable chunked transfer and enforced limits — and be explicit about what you can and cannot promise.

## What the mechanism actually promises `Expect: 100-continue` lets a server decline a request from its headers before the payload moves. That is genuinely valuable for multi-gigabyte uploads: rejecting an unauthenticated or oversized `PUT` after a few hundred bytes instead of after 4 GB saves bandwidth, socket time and, on metered links, money. But the promise is conditional on the whole path cooperating, and it is unenforceable. The failure modes are not errors, they are silent losses of benefit: - A proxy strips `Expect`; the origin never sees it, the client waits its timeout and uploads anyway. Cost: fixed latency, zero benefit. - A proxy relays the request but drops the interim `100`; same outcome from the client's side. - A proxy answers `100 Continue` on its own authority and buffers; the client uploads in full to the proxy, and the origin's later rejection saves nothing on the expensive first leg. - A client library does not implement it, or disables it by default. So the mechanism is an optimization with no floor. Anything you would put in an SLA — 'we never accept more than N bytes', 'unauthenticated uploads consume no bandwidth' — has to be enforced by something else. ## The decision inputs **Decidability from headers.** Only checks answerable from the request line and headers can pay off: authentication and authorization, declared `Content-Length` against a limit, `Content-Type`, target existence, per-tenant quota. Anything requiring the bytes — checksum verification, format validation, malware scanning, deduplication — cannot be short-circuited, so if most rejections are body-dependent the mechanism buys little. **Rejection rate.** The expected saving is roughly (probability of header-decidable rejection) × (body size). If real traffic is rejected 0.1% of the time, paying a round trip on 100% of requests to save 0.1% of transfers is a bad trade at small body sizes and a good one at gigabytes. **Round-trip cost.** One extra RTT per upload. Negligible against a gigabyte, dominant against a kilobyte — which is exactly why clients apply a size threshold rather than a global on/off. **Client population.** If clients are an SDK you ship, you can set the threshold and timeout and know they behave. If they are arbitrary third-party tools, behaviour varies and you must be robust to both. **Path length.** More hops means more chances the expectation is lost. A single-hop path to an origin you operate is the best case. ## Better structural options **Pre-flight authorization.** A small `POST /uploads` that authenticates, checks quota, records intended size and media type, and returns an upload target — often a short-lived pre-signed URL to object storage. All the header-decidable rejections now happen on a tiny request, deterministically, with no dependence on interim responses. This is the pattern the major object stores use, and it also removes the upload traffic from your own service entirely. **Chunked or resumable uploads.** Split the payload into chunks with their own responses, so a rejection costs at most one chunk and interrupted transfers resume instead of restarting. Strictly better than expect-continue for very large payloads because it also solves the failure-recovery problem, which expect-continue does not touch. **Stream cancellation on HTTP/2 and HTTP/3.** The server can reset a stream mid-upload with a final response and `RST_STREAM` rather than waiting for the client to have paused. This is a genuine improvement in the same direction and it does not require a pre-agreed pause, though intermediaries and buffers still make the cutoff imprecise. **Hard server-side limits.** A configured maximum body size at the edge and in the application, enforced by aborting the connection when exceeded, regardless of what `Content-Length` claimed. This is the floor: it must exist because `Content-Length` is client-supplied and can lie, and because chunked bodies declare no length at all. Treating an unbounded upload as a resource-exhaustion vector is the security framing, and no politeness mechanism substitutes for it. ## The position to state Enable expect-continue for large bodies with a threshold and a short timeout; make sure your own edge relays interim responses so the optimization actually fires on the path you control; and design the flow so that correctness, cost control and abuse resistance rest on the pre-flight, the chunking and the hard limits. Then the mechanism is free upside when it works and a bounded, understood cost when it does not.

  • Why can a server not trust Content-Length to enforce an upload size limit?
    It is a client-supplied claim: a client can understate it and then send more, or use chunked transfer encoding and declare no length at all. The header is fine as an early hint for rejecting obviously oversized requests, but the actual limit must be enforced by counting received bytes and aborting when the ceiling is crossed.
  • What does a pre-signed upload URL give you that expect-continue does not?
    Deterministic, header-independent rejection on a tiny pre-flight request, plus the ability to move the bulk transfer off your service to storage that is built for it. It also gives a natural place to encode expiry, size ceiling and content type into the grant, none of which depend on intermediaries honouring an optional interim response.

saying these in an interview costs you the question

  • Promising that unauthenticated uploads never consume bandwidth because the header is enabled
  • Assuming intermediaries reliably relay 1xx interim responses
  • Using the mechanism as the size-limit enforcement point instead of counting bytes
  • Enabling it for small bodies where the extra round trip dominates
  • Overlooking that body-dependent validation cannot be short-circuited at all

context