HTTP 101 Switching Protocols

Switching Protocols

HTTP 101 is Switching Protocols: the server accepts the client’s Upgrade request, and once this response ends, the connection speaks the new protocol (RFC 9110 section 15.2.2). The server must name that protocol in its own Upgrade header and may only pick one the client offered (section 7.8). In practice, a 101 is the WebSocket handshake.

The exchange

RFC 6455’s example:

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-Accept is the base64 SHA-1 of the key followed by the fixed GUID 258EAFA5-E914-47DA-95CA-C5AB0DC85B11. It proves the server understood the handshake rather than echoing a 101 at anything. After the blank line, the bytes are WebSocket frames, not HTTP.

When the handshake fails

A browser that gets anything but a 101 fails the WebSocket and fires error and close; your JavaScript never sees the status. Chrome’s console shows it, as in Error during WebSocket handshake: Unexpected response code: 400.

The usual cause is a proxy. Upgrade and Connection are hop-by-hop headers, and nginx drops both when it proxies unless you pass them on. It also talks HTTP/1.0 to upstreams by default, which has no upgrade:

location /ws/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 1h;    # the default 60s closes a quiet socket
}

HTTP/2 and HTTP/3 have no 101

RFC 9113 section 8.6 says HTTP/2 doesn’t support 101: one connection carries many streams, so it can’t switch protocols. WebSockets over HTTP/2 use extended CONNECT (RFC 8441) with :protocol set to websocket, and success is a 200; RFC 9220 does the same for HTTP/3. The h2c upgrade from HTTP/1.1 to cleartext HTTP/2 is deprecated too. So a 101 is always HTTP/1.1.

Other rules

  • If a request carries both Upgrade and Expect: 100-continue, the server sends the 100 first, then the 101.
  • Upgrade can’t change the transport. HTTP/3 runs over QUIC, so a client finds it through Alt-Svc, never through a 101.
  • Like every 1xx, a 101 is never cached.
import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ noServer: true });

server.on('upgrade', (req, socket, head) => {               // Node: http.Server's upgrade event
  if (req.url !== '/ws/') return socket.destroy();
  wss.handleUpgrade(req, socket, head, (ws) => wss.emit('connection', ws, req));
});

Search 101 426 in the tool below to compare it with Upgrade Required.

Try it in the HTTP Status Codes

101 Switching Protocols

  1. 101Switching Protocols1xx Informational

    The server accepts the client’s Upgrade header and is changing protocols on this connection — HTTP/1.1 to WebSocket, most often. After the blank line the connection is no longer HTTP.

    When to use it

    The handshake that opens a WebSocket, or a move to a newer protocol version, over a connection that already exists.

    Common mistakes

    Agreeing to a protocol the application cannot actually speak, and expecting HTTP to resume afterwards: everything after a 101 belongs to the new protocol.

    Not cacheable by defaultRFC 9110

Every code, by class

Codes marked unofficial come from one server’s own convention — nginx, Cloudflare, cPanel, Microsoft — and are not in the IANA registry.

Runs in your browser. Nothing you enter is sent anywhere.