The WebSocket Protocol
updated by
Extracted elements (30)
Client-to-server masking was introduced to prevent cache-poisoning attacks where a malicious script causes a browser to send crafted bytes through an intermediary proxy. By requiring an unpredictable masking key, the attacker cannot control the exact byte sequence on the wire, preventing the intermediary from misinterpreting the payload as an HTTP response.
The protocol uses minimal framing — only enough to distinguish text from binary frames and to support fragmentation — because metadata is expected to be layered by the application (as HTTP layers on TCP). The design goal was to be as close to raw TCP as is safe given web infrastructure constraints.
The WebSocket opening handshake is a valid HTTP/1.1 Upgrade request, allowing WebSocket clients and HTTP clients to share port 80 (ws) or 443 (wss) on the same server. HTTP proxies that do not support WebSocket will receive a CONNECT-tunneled connection or will proxy the upgrade, depending on configuration.
A client MUST mask all frames sent to the server. The server MUST close the connection upon receiving an unmasked frame and MUST NOT mask any frames it sends to the client. A client MUST close the connection if it detects a masked frame from the server.
All control frames MUST have a payload length of 125 bytes or less and MUST NOT be fragmented. Currently defined control opcodes are Close (0x8), Ping (0x9), and Pong (0xA).
If at any time an endpoint encounters data destined for the UTF-8 decoder that does not conform to the UTF-8 specification (RFC 3629), the endpoint MUST fail the WebSocket connection. Clients MUST NOT show Close frame reason text to end users as it is not guaranteed to be human-readable.
If the wss: URI scheme is used, the client MUST perform a TLS handshake before sending any WebSocket handshake data and MUST use the Server Name Indication (SNI) extension as specified in RFC 6066.
Message fragments MUST be delivered in the order sent, and fragments of one message MUST NOT be interleaved with fragments of another message unless an extension explicitly permits it. Clients and servers MUST support receiving both fragmented and unfragmented messages.
RSV1, RSV2, and RSV3 bits MUST be 0 unless an extension negotiated during the handshake defines meanings for non-zero values. If a nonzero value is received and no negotiated extension defines it, the receiving endpoint MUST fail the WebSocket connection.
The client MUST validate the server's response: status code must be 101, Upgrade header must contain 'websocket', Connection must contain 'Upgrade', and Sec-WebSocket-Accept must match the expected base64-SHA-1 value. Any mismatch requires failing the WebSocket connection.
The client's opening handshake MUST be a valid HTTP/1.1 GET request containing Host, Upgrade: websocket, Connection: Upgrade, Sec-WebSocket-Key (base64-encoded 16-byte nonce), and Sec-WebSocket-Version: 13 header fields.
The masking key MUST be derived from a strong source of entropy (per RFC 4086) and MUST NOT be predictable from prior frame masking keys, to prevent malicious applications from selecting the bytes that appear on the wire.
The Sec-WebSocket-Key header field MUST contain a randomly selected 16-byte nonce that has been base64-encoded, and the nonce MUST be selected randomly for each connection.
There MUST be no more than one WebSocket connection in a CONNECTING state to the same remote host (IP address) and port at any time; the client MUST serialize simultaneous connection attempts to prevent denial-of-service conditions.
When a server receives a Sec-WebSocket-Version value it does not support, it MUST abort the handshake with an appropriate HTTP error (e.g., 426 Upgrade Required) and MUST include a Sec-WebSocket-Version header field listing all versions it is willing to use.
A fragmented message consists of a first frame with FIN=0 and a non-zero opcode, followed by zero or more continuation frames (opcode=0x0, FIN=0), terminated by a continuation frame with FIN=1. All fragments of a message share the type set by the first fragment's opcode.
Subprotocol negotiation uses Sec-WebSocket-Protocol: the client lists acceptable subprotocols in preference order; the server selects at most one and echoes it. If the server returns a subprotocol not offered by the client, the client MUST fail the connection. Subprotocol names SHOULD use reverse-domain naming (e.g., 'chat.example.com') to avoid collisions.
The Close frame (opcode 0x8) optionally carries a 2-byte unsigned integer status code (network byte order) followed by a UTF-8 reason string. Upon receiving a Close frame without having sent one, an endpoint MUST send a Close frame in response, then both endpoints MUST close the underlying TCP connection.
The server constructs Sec-WebSocket-Accept by concatenating the Sec-WebSocket-Key string value with the fixed GUID '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', computing the SHA-1 hash (20 bytes), and base64-encoding the result. This proves the server received the WebSocket-specific handshake.
Upon receiving a Ping frame (opcode 0x9), an endpoint MUST send a Pong frame (opcode 0xA) in response unless it has already sent a Close frame. A Pong sent in response MUST carry the identical application data as the triggering Ping. An endpoint MAY send unsolicited Pong frames.
IANA registered the 'ws' (default port 80) and 'wss' (default port 443) URI schemes, the 'WebSocket' HTTP Upgrade keyword, and five new HTTP header fields: Sec-WebSocket-Key, Sec-WebSocket-Extensions, Sec-WebSocket-Accept, Sec-WebSocket-Protocol, and Sec-WebSocket-Version.
This document creates six new IANA registries: WebSocket Extension Name Registry (Specification Required), WebSocket Subprotocol Name Registry (First Come First Served), WebSocket Version Number Registry, WebSocket Close Code Number Registry, WebSocket Opcode Registry, and WebSocket Framing Header Bits Registry.
Implementations should impose limits on frame and message sizes, header field lengths, and connection counts to mitigate resource exhaustion. Servers can reduce load during attacks by delaying closure to reduce client reconnection rates.
Servers SHOULD validate the Origin header to prevent unauthorized cross-origin use by browser scripts. Non-browser clients can provide any arbitrary Origin value, so origin validation only provides meaningful protection for browser-initiated connections.
SHA-1 is used in the opening handshake to compute Sec-WebSocket-Accept for proof-of-receipt only, not for cryptographic security. Known weaknesses of SHA-1 (collision resistance) do not apply to this non-cryptographic use, as the purpose is merely to confirm the server read the WebSocket-specific handshake.
The strict handshake (Sec-WebSocket-Key/Accept exchange with a fixed GUID) is designed to prevent cross-protocol attacks: pre-existing protocols like SMTP and HTTP cannot accidentally complete a WebSocket handshake, and XMLHttpRequest cannot forge Sec-* headers from a browser context.
A WebSocket connection transitions through states: CONNECTING (initial, during handshake), OPEN (after successful handshake; data exchange permitted), CLOSING (after a Close frame has been sent or received), and CLOSED (after underlying TCP connection is closed). The 'Fail the WebSocket Connection' procedure forces transition to CLOSED.
Masking is performed by XOR: transformed-octet-i = original-octet-i XOR masking-key-octet[i MOD 4], where the masking key is a 32-bit (4-byte) value carried in the frame header. The same algorithm applies for masking and unmasking.
Payload length uses minimal variable-length encoding: 0–125 in the 7-bit field is the direct length; 126 means the following 2 bytes (16-bit unsigned) carry the length; 127 means the following 8 bytes (64-bit unsigned, MSB MUST be 0) carry the length. Lengths MUST use the minimum number of bytes.
The base WebSocket frame header consists of: FIN (1 bit), RSV1-3 (1 bit each), opcode (4 bits), MASK (1 bit), payload length (7 bits, extended to 7+16 or 7+64 bits when the 7-bit value is 126 or 127), an optional 32-bit masking key (present when MASK=1), and variable-length payload data.