Skip to content

100 Continue

What is HTTP 100 Continue?

100 Continue
When you want to send something really big to a website (like a huge video), you...
HTTP 100 Continue status code illustration

Explain Like I’m 3

Before you send something big, you ask ‘Is it okay?’ and someone says ‘Yes, go ahead!’ That’s like saying continue! You check first before sending the big thing.

Example: You want to show your teacher a really big drawing. First you ask ‘Can I bring my big drawing?’ Teacher says ‘Yes, bring it!’ So you bring it over.

Explain Like I’m 5

When you want to send something really big to a website (like a huge video), your computer first asks the website ‘Are you ready for this big thing?’ The website says ‘100 Continue’ which means ‘Yes, I’m ready, send it!’ It’s like asking permission before doing something big. This way, you don’t waste time sending a big file if the website is going to say no anyway.

Example: You want to upload a big video of your birthday party to a website. Your computer first asks ‘Can I send this big 2GB video?’ The website checks and says ‘100 Continue!’ which means it’s ready. Then your computer sends the whole video.

Jr. Developer

HTTP 100 Continue is sent by the server when a client includes the Expect: 100-continue header in a request with a large body. The client sends just the headers first, and if the server responds with 100, it means the server is willing to accept the request body. This prevents wasting bandwidth sending a large payload that would be rejected anyway (due to authentication, file size limits, etc.). The client should wait for 100 before sending the body, or timeout and send anyway. This is primarily useful for large file uploads.

Example: You’re building a file upload feature. A user tries to upload a 500MB video. Your client sends headers with Expect: 100-continue. The server checks authentication and file size limits, then responds with 100 Continue. Only then does the client transmit the actual 500MB video data. If the server had responded 401 Unauthorized instead, you saved 500MB of wasted upload.

Code Example

// Client-side request with Expect header
fetch('/upload/video', {
method: 'POST',
headers: {
'Content-Type': 'video/mp4',
'Content-Length': '524288000',
'Expect': '100-continue'
},
body: largeVideoFile
});
// Server waits for 100 response before sending body
// Most HTTP libraries handle this automatically

Crash Course

100 Continue is an interim response indicating the server has received the request headers and is willing to accept the request body. Clients send Expect: 100-continue when the payload is large or when rejection is likely (authentication, authorization, file size). The handshake works as follows: client sends headers with Expect: 100-continue, server validates the request based on headers alone, server responds with either 100 Continue (proceed) or a final status like 401/413 (reject), and only after receiving 100 does the client send the body. This optimizes bandwidth by avoiding transmission of large payloads that will be rejected. Servers must respond within a reasonable timeout (typically 1-2 seconds), or clients may proceed anyway. Not all clients support this - browsers typically don’t use it, but CLI tools like curl default to sending Expect for requests over 1MB.

Example: A video upload API endpoint receives a PUT request with Expect: 100-continue for a 2GB file. The server checks the Authorization header, verifies the user has upload permissions, checks remaining storage quota, and confirms the Content-Type is acceptable. All checks pass, so it sends 100 Continue. The client then uploads the 2GB file. If auth had failed, the server would send 401 immediately, saving 2GB of bandwidth.

Code Example

// Express.js handling 100-continue
const express = require('express');
const app = express();
app.use((req, res, next) => {
// Handle Expect: 100-continue
if (req.headers['expect'] === '100-continue') {
// Validate before accepting body
if (!req.headers['authorization']) {
return res.status(401).send('Unauthorized');
}
const contentLength = parseInt(req.headers['content-length']);
if (contentLength > 1000000000) { // 1GB limit
return res.status(413).send('Payload Too Large');
}
// Send 100 Continue
res.writeContinue();
}
next();
});
app.post('/upload', (req, res) => {
// Handle the actual upload
res.status(201).json({ message: 'Upload successful' });
});

Deep Dive

The 100 Continue status code, defined in RFC 9110 Section 15.2.1, allows a client sending a request with a body to determine if the origin server will accept the request before transmitting the potentially large request body. The mechanism requires client-server cooperation: the client includes Expect: 100-continue in the request headers, the server must send either an interim 100 response (indicating acceptance) or a final status code (indicating rejection) before reading the request body. Per RFC specifications, the client should wait a reasonable time for the 100 response, but may proceed with sending the body after a timeout (typically 1 second) to handle servers that don’t support this feature.

Technical Details

Protocol-level behavior has important nuances. The Expect: 100-continue mechanism is hop-by-hop: proxies must handle it and not forward the Expect header to the origin if they intend to satisfy the expectation themselves. If a proxy forwards the Expect header, it must forward the 100 response back to the client. The 100 response is an informational interim response and must not be cached. It does not terminate the request/response exchange - the server must still send a final response after receiving the complete request.

RFC 9110 specifies that a server that receives an Expect: 100-continue must respond with either a 100 response or an error status if it can determine the request will fail based on headers alone. Responding with 100 does not guarantee the request will succeed - the server may still reject after receiving the body (e.g., detecting file corruption during upload). The client must be prepared to receive a final status code at any time.

Implementation considerations: Most modern web browsers do not send Expect: 100-continue headers, as the overhead of an extra round trip is considered worse than the risk of wasted bandwidth for typical web requests. However, programmatic clients (curl, wget, mobile apps uploading media) commonly use it for large payloads. HTTP/2 and HTTP/3 handle this differently - they use flow control and stream multiplexing to avoid the need for 100-continue in many cases.

Security implications include potential for amplification attacks: an attacker could send many requests with Expect: 100-continue to consume server resources processing headers without completing requests. Rate limiting on 100 responses may be warranted. The server must be careful not to leak information through timing differences in 100 responses vs. error responses, as this could enable enumeration attacks.

Performance characteristics: For large uploads over high-latency networks, 100-continue saves significant bandwidth when requests are rejected. The extra round trip adds latency but is worthwhile for multi-megabyte payloads. For small requests or low-latency networks, the overhead typically outweighs benefits. Servers should implement timeouts to avoid hanging if a client sends Expect but never sends the body.

Code Example

// Production-grade 100-continue handling with validation
const http = require('http');
const server = http.createServer();
server.on('checkContinue', (req, res) => {
// This event fires when Expect: 100-continue is received
// Validate request BEFORE sending 100 and accepting body
const contentLength = parseInt(req.headers['content-length'] || '0');
const contentType = req.headers['content-type'];
const authToken = req.headers['authorization'];
// Validation 1: Authentication
if (!authToken || !isValidToken(authToken)) {
res.writeHead(401, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Unauthorized' }));
return;
}
// Validation 2: Content-Length
const MAX_SIZE = 1073741824; // 1GB
if (contentLength > MAX_SIZE) {
res.writeHead(413, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: 'Payload Too Large',
maxSize: MAX_SIZE,
received: contentLength
}));
return;
}
// Validation 3: Content-Type
const allowedTypes = ['video/mp4', 'video/quicktime', 'image/jpeg'];
if (!allowedTypes.includes(contentType)) {
res.writeHead(415, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
error: 'Unsupported Media Type',
allowed: allowedTypes
}));
return;
}
// All validations passed - send 100 Continue
res.writeContinue();
// Now process the request body
let body = [];
req.on('data', (chunk) => {
body.push(chunk);
// Safety: Re-check size during upload
const currentSize = Buffer.concat(body).length;
if (currentSize > MAX_SIZE) {
req.destroy();
res.writeHead(413);
res.end('Payload exceeded limit during transfer');
}
});
req.on('end', () => {
const fullBody = Buffer.concat(body);
// Process upload...
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
message: 'Upload successful',
size: fullBody.length
}));
});
});
// Fallback for requests without Expect: 100-continue
server.on('request', (req, res) => {
// Handle normal requests that don't use 100-continue
});
function isValidToken(token) {
// Implement your auth validation
return true;
}
server.listen(3000);

Frequently Asked Questions

When should I use the Expect: 100-continue header?

Use it for large request bodies (typically >1MB) where you want to validate headers (auth, permissions, file size) before transmitting the payload. It's especially useful for file uploads, video streaming, or any scenario where the server might reject the request based on headers alone, saving bandwidth by not sending a large body that will be rejected.

Do browsers send the Expect: 100-continue header?

No, modern browsers typically don't use Expect: 100-continue because the extra round trip adds latency, and web requests are usually small enough that the bandwidth risk is acceptable. However, programmatic clients like curl, mobile apps, and API clients often use it for large uploads.

What happens if the server doesn't respond with 100 Continue?

Clients should implement a timeout (typically 1-2 seconds). If no 100 response is received within the timeout, the client may proceed with sending the request body anyway, assuming the server doesn't support the feature. This ensures compatibility with servers that don't implement 100-continue.

Can the server still reject the request after sending 100 Continue?

Yes! Sending 100 Continue means the server is willing to accept the body based on headers, but it can still reject the request after receiving the body. For example, if the uploaded file is corrupted, contains malware, or fails validation, the server would send an error status as the final response.

How does 100 Continue work with proxies?

The Expect: 100-continue mechanism is hop-by-hop, meaning proxies must handle it themselves. A proxy that receives Expect: 100-continue should either satisfy the expectation itself (sending 100 if validation passes) or forward it to the origin server and relay the 100 response back. Proxies shouldn't blindly forward without understanding the mechanism.

Common Causes

  • Client sending large file upload with Expect: 100-continue header
  • API client using curl or similar tool (curl defaults to Expect for POST >1MB)
  • Mobile app uploading photos/videos and checking server readiness first
  • Programmatic HTTP client implementing bandwidth optimization for large payloads
  • Client anticipating possible rejection due to authentication or authorization issues

Implementation Guidance

  • Server-side: Implement 100-continue handling in your HTTP server/framework
  • Validate request headers (auth, content-type, content-length) before sending 100
  • Send 100 Continue if validation passes, or appropriate error (401, 413, 415) if not
  • Client-side: Set Expect: 100-continue header for requests with large bodies
  • Implement timeout on client (1-2 seconds) to handle servers that don’t support it
  • Don’t use 100-continue for small payloads (overhead outweighs benefits)
  • Ensure proxies in your infrastructure properly handle hop-by-hop Expect header
  • Test your implementation with curl using —expect100-timeout flag

Comments