WebSocket
Normal HTTP is like sending letters: your browser mails a question, the server mails back an answer, and the conversation ends. WebSocket is like a phone call: you connect once and both sides can speak at any moment without waiting for the other to ask first. This is why chat messages appear instantly, live scoreboards update in real time, and your collaborative document shows other people’s cursors moving.
WebSocket, defined in RFC 6455, provides a persistent, full-duplex communication channel over a single TCP connection. It starts as an HTTP/1.1 request and upgrades to the WebSocket protocol, bypassing the request-response model entirely.
Handshake (HTTP Upgrade):
Client request:
GET /ws HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Server response:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=The Sec-WebSocket-Accept is the SHA-1 of the client key concatenated with a fixed GUID (RFC 6455 section 1.3), preventing cache poisoning by non-WebSocket intermediaries.
Frame structure: After the handshake, both sides communicate in lightweight frames (not HTTP). Each frame has:
- FIN bit (last frame in a message)
- Opcode:
0x1text,0x2binary,0x8close,0x9ping,0xApong - Payload length (7, 23, or 71 bits depending on size)
- Masking key (client-to-server frames are masked; server-to-client are not)
WebSocket vs. alternatives:
| Feature | WebSocket | Server-Sent Events (SSE) | HTTP long-polling |
|---|---|---|---|
| Direction | Bidirectional | Server-to-client only | Server-to-client |
| Protocol | Custom over TCP | HTTP/1.1 or HTTP/2 | HTTP |
| Connection | Persistent | Persistent | Repeated reconnect |
| Browser support | Universal | Universal (IE11 needs polyfill) | Universal |
| Best for | Chat, games, collab | Live feeds, notifications | Legacy fallback |
wss:// (WebSocket Secure):
WebSocket over TLS, analogous to HTTPS. Required in production. Shares the same TLS handshake as HTTPS; same certificates apply.
Keepalive:
TCP keepalives may not traverse all load balancers and proxies. The WebSocket protocol includes Ping/Pong frames for application-level keepalives. Clients or servers send a Ping; the other side must respond with a matching Pong.
WebSocket server and client implementation
// Node.js WebSocket server (using the 'ws' library)
import { WebSocketServer, WebSocket } from "ws";
const wss = new WebSocketServer({ port: 8080 });
wss.on("connection", (ws, req) => {
const clientIp = req.socket.remoteAddress;
console.log(`Client connected: ${clientIp}`);
// Send a welcome message
ws.send(JSON.stringify({ type: "welcome", message: "Connected" }));
ws.on("message", (rawData) => {
const message = JSON.parse(rawData.toString()) as { type: string; text: string };
// Broadcast to all connected clients
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({ type: "message", text: message.text }));
}
});
});
// Keepalive ping every 30 seconds
const pingInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) ws.ping();
}, 30_000);
ws.on("close", () => {
clearInterval(pingInterval);
console.log(`Client disconnected: ${clientIp}`);
});
});// Browser WebSocket client with reconnection
function createConnection(url) {
const ws = new WebSocket(url);
ws.addEventListener("open", () => {
console.log("Connected");
ws.send(JSON.stringify({ type: "hello" }));
});
ws.addEventListener("message", (event) => {
const data = JSON.parse(event.data);
console.log("Received:", data);
});
ws.addEventListener("close", (event) => {
console.log(`Disconnected (code ${event.code}). Reconnecting in 3s...`);
setTimeout(() => createConnection(url), 3000);
});
return ws;
}
const socket = createConnection("wss://example.com/ws"); WebSocket powers Slack, Figma, GitHub Codespaces, multiplayer games, trading terminals, and live sports scoreboards. The most common production issue is WebSocket connections dropping behind load balancers: AWS ALB requires explicit WebSocket support enabled, and sticky sessions (session affinity) must be configured so reconnecting clients hit the same backend instance. Nginx proxies WebSocket with proxy_http_version 1.1, proxy_set_header Upgrade $http_upgrade, and proxy_set_header Connection "upgrade". At scale, stateful WebSocket connections are a problem: a single server can hold hundreds of thousands of connections, but horizontal scaling requires a shared pub/sub backend (Redis Pub/Sub, Kafka) so messages from one user reach all instances. Libraries like Socket.IO abstract over WebSocket and provide automatic fallback to long-polling for environments that block WebSocket.