A WebSocket starts as an HTTP request with an Upgrade header, then turns the underlying TCP connection into a full-duplex frame channel. Both sides push whenever they want, no polling, no reconnecting per message. It's the right tool when both sides genuinely have things to say to each other in real time — and overkill for most other things.
GET with Upgrade: websocket; server replies 101 Switching Protocols; the TCP socket then carries WebSocket frames instead of HTTP messages.ws:// (plain) or wss:// (TLS). Always use wss:// in production.A typing indicator, a cursor moving in a shared doc, a price tick, a chess move — anything where either side wants to push instantly and the other side wants to react. Polling that 100× per second would be brutal; with WebSockets it's one open connection and a handful of frames per second.
HTTP/1.1 sends headers on every request — a few hundred bytes minimum. A WebSocket frame is 2–14 bytes plus the payload. For high-frequency tiny messages (cursors, ticks, presence heartbeats), that's a 50–100× reduction in overhead.
Native WebSocket API in every browser since IE 10. No polyfills. No flash fallbacks. Same on iOS, Android, desktop runtimes.
Mobile networks drop. Wi-Fi switches. Corporate proxies kill idle TCP after a few minutes. You need:
A WebSocket is bound to one server process. If user A is connected to server 1 and user B to server 2, and A sends B a message, server 1 can't deliver it directly. You need a fan-out backplane — Redis pub/sub, Kafka, NATS, a cloud broker — that all servers subscribe to. Building this after the connection count grows is painful.
The HTTP Upgrade handshake hits a load balancer; the long-lived TCP connection that follows must keep going to the same backend. Configure session affinity (or use a TCP/L4 load balancer for WebSocket traffic). Sticky sessions complicate blue/green deploys — drain connections gracefully.
What if the server pushes faster than a slow consumer can read? Frames buffer in the OS socket; the buffer fills; the server's writes block. Without explicit back-pressure (drop, coalesce, slow down), one bad client can pin a thread or a connection forever.
The browser WebSocket API doesn't let you set custom headers, so the typical "Authorization: Bearer ..." pattern doesn't work. Options: pass the token as a query param (visible in logs), authenticate after connection on the first frame, or use a short-lived ticket your REST API issues just for the WebSocket handshake.
WebSockets are a transport, not a contract. You bring your own schema (Protobuf, Avro, JSON Schema), your own versioning, your own validation. Caches and CDNs don't help — every byte goes back to your origin.
| Use Case | Why WebSockets |
|---|---|
| Chat & messaging | Both sides push; messages need to arrive instantly. |
| Multiplayer games | Frequent tiny updates from many players; latency matters. |
| Collaborative editing (cursors, edits) | Sub-100ms presence and operational-transform updates. |
| Trading dashboards | High-frequency price ticks, order book updates. |
| Live ops dashboards / observability | Stream metrics and events as they happen. |
| IoT command + telemetry | Devices push sensor data; server pushes commands back. |
For one-way server-to-client streams (notifications, log tail, AI token streaming), Server-Sent Events are simpler and survive proxies more reliably. For request/response with occasional pushes, REST + SSE often beats WebSockets on operational simplicity.
Pick WebSockets when you genuinely need a persistent two-way channel and the team is ready to operate one — heartbeats, reconnect logic, a cross-node fan-out backplane, sticky sessions, capacity planning by connection count, not by RPS.
Otherwise, prefer SSE for one-way streams or polling/long-polling for occasional pushes. Many "we need WebSockets" requirements are actually "we need to push a few events per minute," which doesn't justify the operational tax.