SiteError.comYour friendly guide to HTTP status codes
Status CodesBlog
  1. Home
  2. Blog
  3. Understanding HTTP 101 Switching Protocols: How the WebSocket Handshake Actually Works

Understanding HTTP 101 Switching Protocols: How the WebSocket Handshake Actually Works

September 17, 20268 min read
1xxInformational

101 Switching Protocols is a status code you'll almost never write by hand, yet it's responsible for practically every real-time feature on the modern web — chat, live dashboards, multiplayer games, streaming updates. Any time a connection starts as plain HTTP and ends up as a WebSocket, a 101 happened in between. This post covers what the code actually promises, walks through the WebSocket handshake it powers, and covers a wrinkle that trips people up: HTTP/2 and HTTP/3 don't use this mechanism at all, even for WebSockets.

What Is a 101?

A 101 tells the client the server agrees to change what protocol the current connection speaks — right now, on this same TCP connection, without closing and reopening it.

The 101 (Switching Protocols) status code indicates that the server understands and is willing to comply with the client's request, via the Upgrade header field (Section 7.8), for a change in the application protocol being used on this connection. The server MUST generate an Upgrade header field in the response that indicates which protocol(s) will be in effect after this response. — RFC 9110, Section 15.2.2

In plain English: the client asked (via an Upgrade header on its request) to switch this connection to a different protocol, and the server is saying yes. The response itself must name the protocol being switched to — a bare 101 with no Upgrade header would leave the client not knowing what just happened to its own connection.

Request, Then Response: The Upgrade Dance

A protocol switch is a negotiation, not a unilateral move by either side. The client proposes; the server accepts, declines, or ignores:

A client MAY send a list of protocol names in the Upgrade header field of a request to invite the server to switch to one or more of the named protocols, in order of descending preference, before sending the final response. A server MAY ignore a received Upgrade header field if it wishes to continue using the current protocol on that connection. Upgrade cannot be used to insist on a protocol change. — RFC 9110, Section 7.8

That last line matters: Upgrade is always a request, never a demand, from the client's side. A server has no obligation to honor it — it can just answer the request normally on the existing protocol. (The server-side mirror of "I insist on a different protocol" is 426, not 101 — see below.)

A hypothetical exchange, straight from the RFC:

GET /hello HTTP/1.1
Host: www.example.com
Connection: upgrade
Upgrade: websocket, IRC/6.9, RTA/x11
HTTP/1.1 101 Switching Protocols
Connection: upgrade
Upgrade: websocket
 
[... data stream switches to websocket with an appropriate response
(as defined by new protocol) to the "GET /hello" request ...]

Note that the original request doesn't just vanish once the protocol switches — per the RFC, the server is still expected to answer it, "as if it had received its equivalent within the new protocol," without making the client resend anything.

The WebSocket Handshake, Header by Header

The single most common real-world use of 101 is upgrading an HTTP connection to WebSocket, as defined by RFC 6455. The client's request and the server's response look almost identical to the generic example above, but with WebSocket-specific headers added:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Sec-WebSocket-Key and Sec-WebSocket-Accept exist purely to prove both sides are speaking WebSocket on purpose, not to encrypt or authenticate anything. The server takes the client's key, appends a fixed GUID (258EAFA5-E914-47DA-95CA-C5AB0DC85B11), SHA-1 hashes the result, and base64-encodes it into Sec-WebSocket-Accept. If the value doesn't match what the client independently computes, the client is required to abort the connection — this defends against caches and misconfigured proxies that might otherwise reply to a WebSocket handshake as if it were an ordinary HTTP request.

Handling it in Node.js

You essentially never construct this 101 response yourself. Libraries like ws do the handshake validation and header math for you — you hook into Node's upgrade event and hand the request off:

import { createServer } from "node:http";
import { WebSocketServer } from "ws";
 
const server = createServer();
const wss = new WebSocketServer({ noServer: true });
 
wss.on("connection", (ws) => {
	ws.on("message", (data) => {
		console.log("received:", data.toString());
	});
});
 
server.on("upgrade", (request, socket, head) => {
	wss.handleUpgrade(request, socket, head, (ws) => {
		wss.emit("connection", ws, request);
	});
});
 
server.listen(8080);

handleUpgrade() is what actually writes the 101 Switching Protocols response with the correctly computed Sec-WebSocket-Accept — the library owns the RFC 6455 handshake logic so your application code never touches raw status lines.

Deploying it

WebSocket connections are long-lived by nature, which matters for where you run them. Vercel Functions natively support WebSocket connections, but an established connection stays pinned to a single Function instance for its maximum duration — if your app needs state shared across multiple connections or instances, you'd pair it with an external store (Redis, for example) or use a dedicated realtime platform rather than in-memory state on the function.

HTTP/2 and HTTP/3 Don't Use This At All

Here's the wrinkle: everything above — the Upgrade header, the Connection: Upgrade handshake, the 101 status line — is an HTTP/1.1 mechanism. It doesn't exist in HTTP/2 or HTTP/3. Those protocols already multiplex many logical streams over one connection, so "upgrade this one connection to a different protocol" doesn't map cleanly onto their framing.

Instead, WebSocket over HTTP/2 uses Extended CONNECT, defined in RFC 8441. The client sends a CONNECT request carrying a :protocol pseudo-header set to websocket, rather than an Upgrade header — and per that RFC, the Connection and Upgrade header fields from RFC 6455 "MUST NOT be included" in that request, because the :protocol pseudo-header supersedes them. There's no 101 in this flow at all; the tunnel is established as a CONNECT response instead. RFC 9220 defines the equivalent mechanism for HTTP/3.

Practically, this is invisible to you if you're using a library like ws or a browser's WebSocket API — they negotiate the right mechanism for whatever HTTP version the connection ends up using. But it's worth knowing that "101" and "WebSocket handshake" aren't actually synonymous — 101 is specifically the HTTP/1.1 answer to that question.

101 vs 426

CodeMeaningWho initiatesStandard?
101 Switching ProtocolsServer agrees to a protocol change the client requestedClient proposes via Upgrade; server acceptsYes (RFC 9110)
426 Upgrade RequiredServer refuses to proceed on the current protocol and demands a changeServer insists via Upgrade on an error responseYes (RFC 9110)

They're two ends of the same negotiation: 101 is the server saying yes to an offer, 426 is the server making its own demand. See our 426 Upgrade Required post for the other half of that story.

Common Pitfalls

  1. Hand-rolling the Sec-WebSocket-Accept computation. It's a fixed algorithm (SHA-1 of the client's key plus a magic GUID, base64-encoded), but getting it subtly wrong produces a connection that looks fine in casual testing and then fails against strict clients. Use a maintained library instead of reimplementing RFC 6455's handshake math.
  2. Forgetting the response needs an Upgrade header. The RFC makes it mandatory — a 101 without one is malformed, and a mismatched or missing Upgrade header field is exactly the kind of thing well-behaved clients are required to check for.
  3. Assuming 101 always means WebSocket. It's a generic protocol-switch mechanism; WebSocket is just its most common tenant. HTTP/1.1-to-HTTP/2 upgrades (the h2c token) also used it historically, but RFC 9113 formally deprecated that path — the h2c Upgrade token "MUST NOT be sent by a client or selected by a server." Cleartext HTTP/2 today is negotiated "with prior knowledge" instead, skipping 101 entirely.
  4. Expecting a 101 handshake path on an HTTP/2 or HTTP/3 connection. If you're debugging why a WebSocket library isn't sending an Upgrade request over an HTTP/2 connection, that's expected — it's using Extended CONNECT instead, per RFC 8441.

Wrapping Up

101 is the server's "yes" to a protocol change the client proposed — most visibly, the handshake that turns an ordinary HTTP request into a long-lived WebSocket connection. The rules of thumb:

  • 101 means the server agreed to switch protocols on this connection; the client always proposes first via Upgrade
  • A 101 response is required to include its own Upgrade header naming the new protocol
  • Application code essentially never sends this by hand — WebSocket libraries own the handshake
  • It's an HTTP/1.1-only mechanism; HTTP/2 and HTTP/3 use Extended CONNECT (RFC 8441 / RFC 9220) instead

For more, see our pages on 101 Switching Protocols and 426 Upgrade Required, our 100 Continue post for the other 1xx code most developers meet, and the rest of the 1xx informational codes.

Full Reference

101 Switching Protocols

The server is switching to a different protocol as requested by the client.

Related Status Codes

👂100Continue🔄101Switching Protocols⏳102Processing💡103Early Hints
Back to Blog

Popular Status Codes

  • 200 OK
  • 301 Moved Permanently
  • 302 Found
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error
  • 502 Bad Gateway
  • 503 Service Unavailable

Compare Codes

  • 401 vs 403
  • 301 vs 302
  • 404 vs 410
  • 500 vs 502
  • Compare any codes →

Categories

  • Informational
  • Success
  • Redirection
  • Client Error
  • Server Error
  • NGINX
  • Cloudflare
  • AWS ELB
  • Microsoft IIS

Tools

  • Cheat Sheet
  • Status Code Quiz
  • URL Checker
  • API Playground
  • Blog

© 2026 SiteError.com. All rights reserved.