WebSocket is a protocol that gives a browser and a server a persistent, two-way channel over a single TCP connection, so either side can send messages at any time. It is defined in RFC 6455 from December 2011 and starts as an HTTP request that upgrades the connection. The URL schemes are ws and wss, and the default ports are 80 for ws and 443 for wss, the same as HTTP and HTTPS.
How it works
The client opens a normal HTTP GET with Upgrade: websocket and Connection: Upgrade headers. It adds Sec-WebSocket-Key, a random 16-byte value encoded as Base64, and Sec-WebSocket-Version: 13. A browser also sends an Origin header, which the server can use to decide whether to accept the connection.
The server answers 101 Switching Protocols with a Sec-WebSocket-Accept header. To compute it, append the fixed GUID 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 to the key string, take the SHA-1 digest and Base64-encode it. The check proves the server understood the upgrade. It is not authentication and does not encrypt anything. Use wss, which runs over TLS, for that.
After the 101 response, both sides exchange frames instead of HTTP messages. The opcode field says what a frame carries:
- 0x1 is a text frame and 0x2 is a binary frame.
- 0x8 is close, 0x9 is ping and 0xA is pong.
- Control frames must have a payload of 125 bytes or less.
- Payload length uses 7 bits, or 7+16 bits, or 7+64 bits.
- Every frame from the client to the server is masked with a 32-bit key. Server frames are not masked.
This script computes the Accept value for the sample key in the RFC, then unmasks the RFC's example frame bytes.
const crypto = require("crypto");
const key = "dGhlIHNhbXBsZSBub25jZQ==";
const guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
const accept = crypto.createHash("sha1").update(key + guid).digest("base64");
console.log(`HTTP/1.1 101 Switching Protocols\nUpgrade: websocket\nConnection: Upgrade\nSec-WebSocket-Accept: ${accept}`);
const mask = Buffer.from([0x37, 0xfa, 0x21, 0x3d]);
const masked = Buffer.from([0x7f, 0x9f, 0x4d, 0x51, 0x58]);
console.log(masked.map((b, i) => b ^ mask[i % 4]).toString());
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Hello
What are the WebSocket readyState values?
The readyState property of a browser WebSocket is a number from 0 to 3. CONNECTING is 0, OPEN is 1, CLOSING is 2 and CLOSED is 3. Calling send() while CONNECTING throws an InvalidStateError, and data sent while CLOSING or CLOSED is silently discarded. The close code 1000 means a normal closure.
Common pitfalls
- Not checking Origin: RFC 6455 section 10.2 says servers meant for certain sites should verify Origin and reply 403 Forbidden otherwise. Origin only constrains browsers. Authenticate the connection too, for example with a cookie or token, since non-browser clients can send any value.
- Using ws on a secure page: a page served over HTTPS is generally blocked from opening an unencrypted ws connection. Use wss, which also gives the confidentiality and integrity that RFC 6455 section 10.6 assigns to TLS.
- Proxy drops the upgrade: Upgrade and Connection are hop-by-hop headers, so a reverse proxy drops them unless told otherwise, and the handshake fails. Configure it to pass them. The Nginx Reverse Proxy Generator can scaffold a config with WebSocket support.
- Idle connections closing: proxies close silent connections after a timeout. By default, Nginx closes a proxied connection when the backend sends nothing for 60 seconds. The RFC notes a ping frame can serve as a keepalive, so send pings or application heartbeats.
- Treating the Accept check as security: it only confirms the server speaks WebSocket. Authentication, authorization and message validation are still your job.
- Forgetting to reconnect: the WebSocket standard defines no automatic reconnection. Handle the close event and resubscribe after a new connection.
Related terms
- HTTP — the handshake is an HTTP/1.1 request that upgrades the connection.
- HTTPS — wss uses the same TLS layer and port 443.
- TLS — encrypts a wss connection.
- SHA-1 — the hash used in the Sec-WebSocket-Accept calculation.
- Base64 — encodes the key and the Accept value.
- REST — request and response style that WebSocket replaces for live updates.
See also
- Tool: Nginx Reverse Proxy Generator — scaffolds Nginx config for routing to local ports with WebSocket support.
- Cheatsheet: HTTP Headers Reference — look up request and response headers such as those in the handshake.