WebSocket Protocol
HTTP is a request-response protocol. The client speaks, the server answers, and the connection closes. This works well for fetching documents, but it is a poor fit for anything that needs the server to push data unprompted — stock tickers, chat, live dashboards, collaborative editing.
Before WebSocket, developers worked around HTTP's limitations with polling (asking the server "anything new?" on a timer), long-polling (holding a request open until the server has something to say), and chunked transfer tricks. All of these are awkward. WebSocket, standardised in RFC 6455 (2011), adds a proper full-duplex channel to the web platform.
The HTTP/1.1 handshake
A WebSocket connection begins as a plain HTTP/1.1 request. The client asks the server to upgrade the connection, and if the server agrees, they switch protocols on the same TCP socket. No new connection is needed.
GET /chat HTTP/1.1
Host: 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=
The Sec-WebSocket-Key is a random 16-byte value the client sends as a
Base64 string. The server proves it understands the WebSocket protocol — rather than just
forwarding the request — by appending the magic GUID
258EAFA5-E914-47DA-95CA-C5AB0DC85B11
taking the SHA-1 hash, and returning
the Base64 result as Sec-WebSocket-Accept. After the
101 Switching Protocols response, the HTTP layer is discarded and both sides
speak the WebSocket framing protocol directly.
The HTTP/2 handshake
HTTP/2 is a binary, multiplexed protocol — multiple request/response streams share a single
TCP connection concurrently. The HTTP/1.1 upgrade mechanism does not translate: HTTP/2
forbids Connection and Upgrade headers, and the
101 Switching Protocols status code does not exist in HTTP/2.
RFC 8441 (2018) solves this with an
extended CONNECT method. The client adds a :protocol
pseudo-header to signal the desired application protocol, leaving the existing HTTP/2
stream open rather than hijacking the underlying TCP connection.
Before the client can use this, the server must opt in by sending
SETTINGS_ENABLE_CONNECT_PROTOCOL = 1 in its HTTP/2 SETTINGS frame. Once
enabled, the handshake looks like this:
:method = CONNECT
:protocol = websocket
:scheme = https
:path = /chat
:authority = example.com
sec-websocket-version = 13
:status = 200
A 200 response establishes the WebSocket. The HTTP/2 stream is then treated
as if it were a TCP connection — WebSocket frames flow through it directly.
Sec-WebSocket-Key and Sec-WebSocket-Accept are dropped; the
:protocol pseudo-header takes over their role. However, these headers and the
handling of them may still need to be performed if the HTTP/2 connection is actually an HTTP/1.1
connection that was upgraded by a loadbalancer before reaching your app server.
Benefits over HTTP/1.1
- Multiplexing. Multiple WebSocket streams share one TCP connection alongside regular HTTP/2 traffic. No extra TCP handshake per WebSocket.
- Stream-level flow control and priorities. HTTP/2 stream priorities and RST_STREAM cancellation apply directly to each WebSocket stream.
- No cache-poisoning risk. HTTP/2 is binary end-to-end. Intermediaries that speak HTTP/2 parse the binary framing correctly and cannot be tricked by crafted payload bytes the way HTTP/1.1 text proxies can. Client-to-server masking remains required by RFC 6455 §10.3, but its original defense against proxy cache poisoning is irrelevant over a properly-terminating HTTP/2 connection.
-
Connection not dedicated. Unlike HTTP/1.1 where
Upgrade: websocketdedicates the entire connection to WebSocket, HTTP/2'sCONNECTuses a single stream. The connection remains multiplexed—other HTTP requests can coexist on the same connection.
The frame format
Everything sent over a WebSocket connection — text messages, binary payloads, pings, close signals — travels inside a frame. Frames are small: their header is just two bytes in the common case. Scroll through the sections below to see each field of the header reveal itself.