WebSockets Introduction
Full-duplex vs HTTP request/response, the upgrade handshake, and when (not) to use WebSockets.
What WebSockets are
WebSocket is a communication protocol that provides a persistent, full-duplex connection between a client (usually a browser) and a server over a single TCP connection. "Full-duplex" means both sides can send messages to each other at any time, independently — unlike the request/response pattern that defines ordinary HTTP.
Full-duplex vs HTTP request/response
Ordinary HTTP (including AJAX/fetch() calls) follows a strict pattern: the client sends a request, the server sends back exactly one response, and the exchange is over. If the server needs to tell the client something new — a chat message just arrived, a stock price changed — it has no way to do so on its own; the client would have to ask again.
HTTP request/response (half-duplex, one exchange at a time):
Client -----> request -----> Server
Client <----- response <----- Server
(connection then closes, or is reused for the NEXT separate request)
A WebSocket connection, once established, stays open, and either side can send a message at any moment without waiting for a request:
WebSocket (full-duplex, connection stays open):
Client <===================> Server
(either side can send a message at any time,
over the same long-lived connection)
This is the fundamental shift: HTTP is built around the client always initiating, and the server only ever replying. WebSockets let the server push data to the client whenever it wants, without the client having to ask first.
The handshake
A WebSocket connection starts life as a completely ordinary HTTP request, which then asks to be upgraded to the WebSocket protocol:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
If the server supports WebSockets on that path, it responds with a 101 Switching Protocols status, and from that point on, the underlying TCP connection stops speaking HTTP entirely and starts speaking the WebSocket framing protocol instead:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
This is why a WebSocket URL uses its own schemes — ws:// (unencrypted, analogous to http://) and wss:// (encrypted over TLS, analogous to https://) — even though the connection begins as a regular HTTP request under the hood:
const socket = new WebSocket('wss://example.com/chat');
When to use WebSockets
WebSockets earn their complexity when a feature genuinely needs frequent, low-latency, bidirectional communication:
- Chat applications — messages must appear for other participants immediately, without them refreshing or polling.
- Live dashboards — a stock ticker, a live sports score, a server monitoring graph updating in real time.
- Multiplayer / collaborative features — a multiplayer game's state, or multiple users editing the same document and seeing each other's cursors live.
- Live notifications — a "someone just replied" indicator appearing without a page refresh.
When NOT to use WebSockets
Most applications are not chat apps or live dashboards, and reaching for WebSockets by default adds real cost: a persistent connection per client is more expensive to hold open at scale than stateless HTTP requests, and it introduces new problems (see the next page on scaling) that a plain request/response API simply doesn't have.
- Standard CRUD apps — creating a blog post, updating a user's profile, loading a product page. A normal
fetch()request/response is simpler, easier to cache, easier to scale, and easier to debug. - Infrequent updates — if "real-time" really means "updates every few minutes are fine," a periodic
fetch()(or even a simple page refresh) is far less operationally complex than maintaining persistent connections. - One-directional server pushes only — if the server needs to push updates but the client never needs to send anything back on the same channel, Server-Sent Events (a simpler, HTTP-based one-way push mechanism) is often a better, lighter-weight fit than a full bidirectional WebSocket.
Common mistakes
- Reaching for WebSockets as a default "real-time" solution when the actual requirement is just "update every so often" — a periodic
fetch()poll is simpler and scales more predictably for infrequent updates. - Forgetting that a WebSocket connection is stateful and pinned to whichever server instance accepted it — this has real implications once you run more than one server (covered in the next page).
- Not planning for reconnection — networks drop, laptops sleep, mobile connections switch from WiFi to cellular — a production WebSocket client needs reconnect logic, not just a single
new WebSocket(url)call.
Interview questions
Q: What does "full-duplex" mean, and how does it differ from ordinary HTTP request/response? Full-duplex means both sides of a connection can send messages to each other independently and at any time. Ordinary HTTP is built around the client always initiating a request and the server always replying exactly once — the server has no mechanism to push new information to the client on its own. A WebSocket connection stays open after an initial handshake, letting either side send messages whenever it needs to, without waiting to be asked.
Q: How does a WebSocket connection get established, given that browsers only speak HTTP by default?
It begins as a normal HTTP request carrying an Upgrade: websocket header. If the server supports it, it responds with 101 Switching Protocols, and from that point on the same underlying TCP connection switches from HTTP framing to the WebSocket protocol's framing — no new connection is opened, the existing one is repurposed.