WebSocket

WebSocket is a communications protocol providing full-duplex, bidirectional communication over a single TCP connection. The protocol operates over HTTP ports 80 and 443, maintaining compatibility with existing HTTP infrastructure including proxies and intermediaries.

Baseline: Widely available

Supported in all major browsers. webstatus.dev

Usage

WebSocket and HTTP are distinct protocols sharing the same underlying transport infrastructure. The WebSocket protocol addresses a core limitation of HTTP: the lack of persistent, bidirectional communication.

With HTTP, a client such as a web-based chat application must continuously poll the server for new messages. Each poll creates a new request, and in some configurations, a new connection. WebSocket maintains a single long-lived connection for the duration of the session, eliminating the overhead of repeated connection setup and teardown.

Because WebSocket connections ride on existing HTTP infrastructure, proxies, load balancers, and Authentication schemes work without major changes. The protocol is independent of HTTP at the data-transfer level. Once established, WebSocket frames flow directly over TCP without HTTP framing.

URI schemes

The WebSocket protocol defines two URI schemes: ws:// for unencrypted connections and wss:// for WebSocket Secure connections encrypted with TLS.

ws://authority/path?query
wss://authority/path?query

No fragment support

WebSocket URIs follow standard URI syntax but do not support the fragment component (#).

Well-known URIs

The well-known URI mechanism extends to the ws and wss schemes. The list of registered well-known URIs is maintained by IANA.

Establishing a connection over HTTP/1.1

The HTTP/1.1 protocol provides a mechanism for upgrading an existing connection to WebSocket using the HTTP Upgrade request header. The client sends an HTTP GET request with the Upgrade and Connection headers to signal a protocol switch.

The server is not obligated to accept the upgrade, even when supporting the WebSocket protocol. The Upgrade header is hop-by-hop, meaning each intermediary along the path processes the header individually.

HTTP/1.1 handshake example

The client sends an HTTP request containing the Upgrade header, the Connection header with the Upgrade token, a Sec-WebSocket-Key, and Sec-WebSocket-Version. The server responds with 101 Switching Protocols and includes Sec-WebSocket-Accept to confirm the protocol switch.

Request

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

Response

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

After the client verifies the Sec-WebSocket-Accept value, subsequent data flows as WebSocket frames rather than HTTP messages.

Establishing a connection over HTTP/2

HTTP/2 does not support connection-wide headers like Upgrade or status codes like 101. Instead, an extended CONNECT method opens a WebSocket tunnel on a single HTTP/2 stream.

The server advertises support by sending the SETTINGS_ENABLE_CONNECT_PROTOCOL parameter (0x08) with a value of 1. The client then sends a CONNECT request with the :protocol pseudo-header set to websocket, along with :scheme, :path, and :authority pseudo-headers.

:method = CONNECT
:protocol = websocket
:scheme = https
:path = /chat
:authority = www.example.re
Sec-WebSocket-Version: 13
Origin: https://example.re

This approach allows WebSocket traffic to share a single TCP connection with regular HTTP/2 streams, benefiting from multiplexing, prioritization, and flow control.

Establishing a connection over HTTP/3

The same extended CONNECT mechanism applies to HTTP/3. The semantics of the pseudo-headers and the SETTINGS_ENABLE_CONNECT_PROTOCOL setting (0x08) are identical to the HTTP/2 variant.

The main difference lies in stream closure. Orderly closure maps to a FIN on the HTTP/3 stream, while exceptional termination uses a stream error of type H3_REQUEST_CANCELLED.

WebSocket headers

Several headers negotiate parameters during the opening handshake.

Sec-WebSocket-Key

The Sec-WebSocket-Key header carries a nonce consisting of a randomly selected 16-byte value encoded in base64. A fresh key is generated for each connection. The purpose is not Authentication or privacy. Rather, the key confirms the server understands the WebSocket protocol and prevents intermediary caches from replaying stale upgrade responses.

Sec-WebSocket-Accept

The Sec-WebSocket-Accept header appears in the server response during the handshake. The server concatenates the client Sec-WebSocket-Key with the fixed GUID 258EAFA5-E914-47DA-95CA-C5AB0DC85B11, computes the SHA-1 hash of the result, and returns the hash as a base64-encoded string.

Not a security mechanism

The Sec-WebSocket-Key / Sec-WebSocket-Accept exchange verifies protocol understanding. A different random key is sent for each connection so cached responses from intermediaries are detected as invalid.

Sec-WebSocket-Extensions

The Sec-WebSocket-Extensions header lets the client request protocol-level extensions. Multiple extensions are listed in a comma-delimited format, and each extension accepts optional parameters separated by semicolons.

The server responds with the same header, confirming which extensions will be active for the duration of the connection. Extensions are registered in the IANA WebSocket Extension Name Registry.

Sec-WebSocket-Extensions example

The most widely implemented extension is permessage-deflate, which compresses message payloads using the DEFLATE algorithm.

Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

Sec-WebSocket-Protocol

The Sec-WebSocket-Protocol header lets the client request a specific application-level subprotocol. Multiple subprotocols are listed in order of preference, either as separate header instances or in a comma-delimited list. Subprotocols are registered in the IANA WebSocket Subprotocol Name Registry.

The server responds with a single Sec-WebSocket-Protocol header confirming the selected subprotocol.

Sec-WebSocket-Protocol example

A home automation network negotiates a subprotocol for communicating with IoT devices. The preferred protocol is SHIP (Smart Home IP), with done (done.best IoT protocol) as a fallback.

Sec-WebSocket-Protocol: ship, done

Sec-WebSocket-Version

The Sec-WebSocket-Version header specifies which WebSocket protocol version the client supports. The value is a non-negative integer between 0 and 255. The current standard version is 13.

When the server does not support the requested version, the response includes Sec-WebSocket-Version listing all versions the server accepts. The response status code is typically 426 Upgrade Required.

Sec-WebSocket-Version example

The client requests version 13 (current standard) with version 8 as a fallback.

Sec-WebSocket-Version: 13, 8

If the server supports neither, a response indicates the versions available.

Response

HTTP/1.1 426 Upgrade Required
Sec-WebSocket-Version: 7, 6

Framing

Once the handshake completes, data travels in WebSocket frames rather than HTTP messages. Each frame carries an opcode identifying its type:

Opcode Type
0x1 Text frame
0x2 Binary frame
0x0 Continuation frame
0x8 Close
0x9 Ping
0xA Pong

Text frames carry UTF-8 data. Binary frames carry arbitrary bytes. Close frames initiate a graceful shutdown and carry an optional two-byte status code followed by a UTF-8 reason string.

Ping and Pong frames serve as a keepalive mechanism. An endpoint receiving a Ping responds with a matching Pong.

Accessing WebSocket programmatically

WebSocket connections are created through the browser API or through server-side libraries.

const connection = new WebSocket(
  'ws://files.example.re/'
);

Libraries for Java, C#, Python, Rust, Go, Ruby, and other languages provide equivalent functionality for connection management, message framing, and event handling.

Takeaway

The WebSocket protocol provides full-duplex, bidirectional communication over a single TCP connection. A connection is established through the HTTP/1.1 Upgrade mechanism, through the extended CONNECT method in HTTP/2 and HTTP/3, or programmatically through language-specific APIs.

See also

Last updated: March 11, 2026