Large HTTP uploads often fail before the body is worth sending, especially when an API, proxy, or object store rejects the request during authentication, quota, or policy checks. Controlling Expect: 100-continue in cURL keeps those failures predictable and reduces wasted bandwidth on retries and rejected uploads.
On HTTP/1.1 request paths, cURL can send Expect: 100-continue before the request body so the server can either return 100 Continue or reject the request early. cURL adds this automatically for PUT uploads and for POST bodies that are known or suspected to be larger than one megabyte, while –header “Expect:” disables it, –header “Expect: 100-continue” forces it, and –expect100-timeout changes how long cURL waits after sending the header.
The handshake only matters on HTTP/1.1, and some gateways or proxies delay it enough that the default wait becomes a visible stall. Keep verbose or trace output available while standardizing an upload pattern, because the correct setting depends on whether the upstream reliably answers with 100 Continue or performs better when the body is sent immediately.
Related: How to use HTTP/2 in cURL
Related: How to use HTTP/3 with cURL
Steps to control Expect: 100-continue handling in cURL:
- Run a verbose HTTP/1.1 upload and confirm whether cURL adds Expect: 100-continue before sending the body.
$ curl --verbose --http1.1 \ --upload-file ./release-bundle-2026.03.29.tar.gz \ --output /dev/null \ --write-out "HTTP %{http_code}\n" \ "https://objects-api.example.net/v1/releases/release-bundle-2026.03.29.tar.gz" > PUT /v1/releases/release-bundle-2026.03.29.tar.gz HTTP/1.1 > Host: objects-api.example.net > User-Agent: curl/8.x > Accept: */* > Content-Length: 1048704 > Expect: 100-continue < HTTP/1.1 100 Continue < HTTP/1.1 201 Created HTTP 201
On HTTP/1.1 PUT uploads, cURL adds the header automatically unless it is overridden.
Related: How to upload files with cURL
- Remove the handshake with an empty Expect: header when the server or proxy handles immediate body transmission more reliably.
$ curl --verbose --http1.1 \ --header "Expect:" \ --upload-file ./release-bundle-2026.03.29.tar.gz \ --output /dev/null \ --write-out "HTTP %{http_code}\n" \ "https://objects-api.example.net/v1/releases/release-bundle-2026.03.29.tar.gz" > PUT /v1/releases/release-bundle-2026.03.29.tar.gz HTTP/1.1 > Host: objects-api.example.net > User-Agent: curl/8.x > Accept: */* > Content-Length: 1048704 * upload completely sent off: 1048704 bytes < HTTP/1.1 201 Created HTTP 201
Removing the header means a rejected large upload can still send the full body before the failure becomes visible.
- Force the provisional check on a small POST body when the upstream should approve the request before reading payload bytes.
$ curl --verbose --http1.1 \ --header "Expect: 100-continue" \ --header "Content-Type: application/json" \ --data-binary @./release-bundle-2026.03.29.json \ --output /dev/null \ --write-out "HTTP %{http_code}\n" \ "https://objects-api.example.net/v1/releases/release-bundle-2026.03.29/metadata" > POST /v1/releases/release-bundle-2026.03.29/metadata HTTP/1.1 > Host: objects-api.example.net > User-Agent: curl/8.x > Accept: */* > Expect: 100-continue > Content-Type: application/json > Content-Length: 86 < HTTP/1.1 100 Continue < HTTP/1.1 201 Created HTTP 201
Setting the header explicitly applies the same approval step to small request bodies that would normally be sent immediately.
Related: Send data in HTTP requests with cURL
- Shorten the wait window with –expect100-timeout when the server accepts the upload but the default pause adds avoidable latency.
$ curl --verbose --http1.1 \ --expect100-timeout 0.2 \ --upload-file ./release-bundle-2026.03.29.tar.gz \ --output /dev/null \ --write-out "HTTP %{http_code}\n" \ "https://objects-api.example.net/v1/releases/release-bundle-2026.03.29.tar.gz" > PUT /v1/releases/release-bundle-2026.03.29.tar.gz HTTP/1.1 > Host: objects-api.example.net > User-Agent: curl/8.x > Accept: */* > Content-Length: 1048704 > Expect: 100-continue * Done waiting for 100-continue < HTTP/1.1 100 Continue < HTTP/1.1 201 Created HTTP 201
–expect100-timeout accepts decimal seconds, and cURL sends the body when the timer expires even if the provisional response arrives later.
- Capture a trace and verify the final policy before baking it into automation or CI jobs.
$ curl --trace-ascii curl-expect-upload.trace --http1.1 \ --upload-file ./release-bundle-2026.03.29.tar.gz \ --output /dev/null \ "https://objects-api.example.net/v1/releases/release-bundle-2026.03.29.tar.gz" $ grep -nE 'Expect: 100-continue|HTTP/1.1 100 Continue|HTTP/1.1 201 Created' curl-expect-upload.trace 11:0079: Expect: 100-continue 14:0000: HTTP/1.1 100 Continue 16422:0000: HTTP/1.1 201 Created
The trace is complete when it shows the request header policy, any provisional response, and the final application response in the same transfer.
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.
