< HTTP/1.1 100 Continue100 Continue
The server received the request headers and the client should proceed to send the body. Learn when and how to use 100 Continue for efficient large uploads.
- Cacheable
- n/a (interim or validation response)
- Client action
- Send the request body
- Usually sent by
- Origin server (after Expect: 100-continue)
- Spec
- RFC 9110 §15.2.1
On this page
TL;DR:
100 Continueis the server saying “headers look fine, send the body.” A client opts in by sendingExpect: 100-continueand pausing before a large upload. It’s an interim response with no body; the real result arrives in the final response that follows, which might be an error.
When Does This Happen?
Say a client wants to upload a 2 GB file. It sends the request headers with Expect: 100-continue and pauses. If the server can already tell it will reject the request (wrong credentials, a size limit, an unsupported type), it sends that final error right away. Otherwise it sends 100 and reads the body. Either way, nobody wastes bandwidth on a body that was never going to be accepted.
Per RFC 9110 §10.1.1, the client doesn’t have to wait forever; after a timeout it sends the body anyway. A server can also skip the 100 if some of the body has already arrived. And a 100 only means “keep going”. Authentication, quota and content checks can fail once the server reads the body.
Example Responses
Here’s a trimmed HTTP/1.1 exchange with a five-byte body. The client sends its headers and waits:
POST /upload HTTP/1.1
Host: localhost:8081
Content-Type: application/octet-stream
Content-Length: 5
Expect: 100-continue
The server answers with the interim response. The blank line ends it:
HTTP/1.1 100 Continue
The client sends hello, and once the server has read it, it returns the final status:
HTTP/1.1 204 No Content
Neither 100 nor 204 has a body. A real upload endpoint might just as well return an error here after validating what it received.
Getting 100 Continue right
Node’s HTTP server sends 100 automatically when it sees Expect: 100-continue, unless you register a checkContinue listener. By the time your normal request handler runs, the 100 has already gone out, so that’s too late to reject on headers.
Once you handle checkContinue, Node skips the usual request event for those requests, so you have to route accepted ones to your body handler yourself. See the Node HTTP documentation.
Implementation Examples
Save this as continue-demo.mjs and run node continue-demo.mjs. It accepts one route and one media type, throws the body away, and returns 204. There’s no storage or authentication; it’s just for watching the exchange.
import http from 'node:http';
function accept(req, res) {
const status = req.method !== 'POST' || req.url !== '/upload'
? 404
: req.headers['content-type'] !== 'application/octet-stream'
? 415
: 0;
if (!status) return true;
res.writeHead(status, { Connection: 'close' });
res.end();
return false;
}
function consume(req, res) {
req.on('end', () => {
res.writeHead(204);
res.end();
});
req.resume();
}
const server = http.createServer((req, res) => {
if (accept(req, res)) consume(req, res);
});
server.on('checkContinue', (req, res) => {
if (!accept(req, res)) return;
res.writeContinue();
consume(req, res);
});
server.listen(8081, '127.0.0.1');
On the client side, Node fires a continue event when the 100 arrives. Pair it with your own fallback timer. Save this as continue-client.mjs and run it while the demo server is up:
import http from 'node:http';
const body = Buffer.from('hello');
let sent = false;
let final = false;
let timer;
const req = http.request('http://127.0.0.1:8081/upload', {
method: 'POST',
headers: {
Expect: '100-continue',
'Content-Type': 'application/octet-stream',
'Content-Length': body.length,
},
}, (res) => {
final = true;
clearTimeout(timer);
console.log('final', res.statusCode);
res.resume();
res.on('end', () => {
if (!sent) req.destroy();
});
});
function sendBody() {
if (sent || final) return;
sent = true;
clearTimeout(timer);
req.end(body);
}
req.once('continue', sendBody);
req.on('error', (error) => {
clearTimeout(timer);
console.error(error.message);
});
req.once('close', () => clearTimeout(timer));
timer = setTimeout(sendBody, 1000);
req.flushHeaders();
flushHeaders() pushes the headers out without the body. If you called req.end(body) right away instead, the body would go out before the server had a chance to decide. The one-second timer is this example’s choice; Node doesn’t impose one. The sent guard stops a late 100 from sending the body a second time after the timer already fired. Change the Content-Type to text/plain to see the server’s immediate 415.
With a proxy in the middle, the proxy might send the 100 itself and the origin might reject the upload later. The 100 your client saw only tells you about that one connection. To debug, capture both legs or line up the proxy and origin logs, and judge success by the final status and what the origin actually stored.
Try It Yourself
Use curl against that local server:
printf 'hello' > /tmp/continue-body.txt
curl --http1.1 -v --expect100-timeout 1 \
-H 'Expect: 100-continue' \
-H 'Content-Type: application/octet-stream' \
--data-binary @/tmp/continue-body.txt \
http://127.0.0.1:8081/upload
Run it again with Content-Type: text/plain and the server rejects the request before sending 100. curl’s verbose trace shows both the interim and the final status.
curl’s --expect100-timeout defaults to one second. After that, it sends the body whether or not a 100 showed up. The option only sets the wait; the command above adds the Expect header explicitly.
To compare, send the same upload with the header suppressed:
curl --http1.1 -v -H 'Expect:' \
-H 'Content-Type: application/octet-stream' \
--data-binary @/tmp/continue-body.txt \
http://127.0.0.1:8081/upload
This one goes through the server’s ordinary request path. In the trace you’ll see the body go out right after the headers, with no pause.
Common Mistakes
HTTP doesn’t define how long a client should wait for the 100. Each client picks its own timeout, so set it in the client you’re using. If a server returns 417 Expectation Failed, RFC 9110 says to retry without the expectation, which just sends the body straight away.
You can’t test this from a browser. Fetch won’t let you set Expect, because it’s a forbidden request header. Use curl or another HTTP client.
100 vs Other Informational Codes
100 is about the request body: go ahead and send it. 103 Early Hints is about the response: here are resources to start loading while the server finishes. Both are interim, and neither has a body.
Related Status Codes
Frequently asked questions
What does 100 Continue mean?
100 Continue is an interim response telling a client that sent Expect: 100-continue to go ahead and upload the body. It has no body of its own, and the final response comes later and can still be an error.
When should I use Expect: 100-continue?
Use it for large uploads that the server might reject based on headers alone, such as auth, size or content type. Waiting a moment for that verdict saves sending a body that would be thrown away.
What happens if the server does not support 100 Continue?
The client stops waiting after a timeout and sends the body anyway. curl waits one second by default; other clients pick their own timeout. If the server answers 417, retry the request without the Expect header.
Is 100 Continue a final response?
No. 100 is interim and has no body. A final response always follows, and it reports the real outcome, success or failure.
Sources
Related
101 Switching Protocols
The server is switching to a different protocol as requested by the client. Learn about WebSocket upgrades and protocol negotiation.
102 Processing
The server has accepted the request and is processing it, but no response is available yet. Learn about this WebDAV status code for long-running operations.
Expect Header: 100-continue, curl Delays and 417
Expect: 100-continue asks the server to approve a request before the body is sent. Why curl adds it past 1 MB, the 1s delay, -H "Expect:", and 417.
103 Early Hints
The server sends preliminary response headers to help the client start preloading resources. Learn how 103 Early Hints improves page load performance.