Skip to content

The WebSocket Handshake

A WebSocket does not appear out of thin air. It starts life as a perfectly ordinary HTTP request and is then upgraded in place. Understanding this handshake demystifies a great deal: why WebSockets travel on the same ports as web traffic, why they pass through most proxies and firewalls, and why the ws:// and wss:// schemes mirror http:// and https://.

The client opens a normal TCP connection and sends an HTTP GET request — but with special headers that say “I would like to switch this connection to the WebSocket protocol.” The two headers that do the real work are Upgrade: websocket and Sec-WebSocket-Key.

GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: https://example.com

A few of these earn their place:

  • Upgrade: websocket and Connection: Upgrade together signal the protocol switch.
  • Sec-WebSocket-Key is a random, base64-encoded value the client generates fresh for this connection. It is not a security secret; it exists so the client can confirm the response came from a server that genuinely understood the WebSocket handshake.
  • Sec-WebSocket-Version: 13 pins the protocol version (13 is the standard one).

If the server speaks WebSocket and accepts, it replies with status 101 Switching Protocols — an HTTP status that means exactly “we are done with HTTP semantics on this connection; from here on it is a different protocol.”

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

The magic is in Sec-WebSocket-Accept. The server takes the client’s Sec-WebSocket-Key, appends a fixed, well-known string defined by the spec, hashes the result with SHA-1, and base64-encodes it. The client computes the same value and checks for a match. If they agree, the client knows it is talking to a real WebSocket server and not, say, a cache that blindly returned a 101. No match, no connection.

sequenceDiagram
  participant C as Client
  participant S as Server
  C->>S: TCP connection established
  C->>S: GET /chat HTTP/1.1 (Upgrade: websocket, Sec-WebSocket-Key)
  Note over S: validate headers, compute Sec-WebSocket-Accept
  S-->>C: HTTP/1.1 101 Switching Protocols (Sec-WebSocket-Accept)
  Note over C,S: same TCP connection — now speaking WebSocket
  C->>S: data frame
  S->>C: data frame
The upgrade from HTTP request to live WebSocket

Notice what the diagram does not show: a second connection. After the 101, the client and server keep using the same TCP connection they already had. Nothing is reopened. The HTTP request was simply the doorway; once through it, both sides stop exchanging HTTP messages and start exchanging WebSocket frames (the subject of the next lesson).

Because the handshake is an HTTP request, a WebSocket connection can ride on ports 80 and 443 alongside normal web traffic, reuse existing TLS for wss://, and slip through the proxies and load balancers that already understand HTTP. The protocol gets the reach of the web for free, then sheds HTTP’s request/response model the instant it no longer needs it.

Not validating Origin during the handshake Symptoms:

  • Accepting every WebSocket connection without checking the Origin header
  • Opens the door to cross-site WebSocket hijacking (CSWSH)
  • Check the Origin header against an allowlist before responding with 101

Authenticating after the connection is already open Symptoms:

  • Accepting the handshake first and authenticating later leaves the connection open, unverified, for a window of time
  • Attach an auth token to the handshake (query param or header) and validate it before responding with 101

💡 Real-world example

Binance WebSocket API:

  • The real endpoint is wss://stream.binance.com:9443 (Binance API Docs — WebSocket Streams)
  • Streams that need account data (user data streams) validate the API key before allowing a subscription

General principle:

  • A proxy or corporate firewall must understand the Upgrade/Connection headers during the handshake, or it will treat the request as plain HTTP and block it
What HTTP status code does the server return to accept a WebSocket upgrade?
What is the purpose of Sec-WebSocket-Key and Sec-WebSocket-Accept?
After the 101 response, what happens to the connection?