Q04 — Why do WebSockets exist — what does HTTP get wrong for real-time communication?
What is HTTP's fundamental model?
HTTP is built on a simple contract: the client asks, the server answers. Every interaction is initiated by the client. The server has no mechanism to reach out to the client unprompted — it can only respond to requests it receives.
This is called a request-response model, and it works perfectly for most of the web: you ask for a page, you get the page. You submit a form, you get a result. The client always drives.
But the modern web has applications where the server needs to push information to the client — chat messages, live scores, stock prices, collaborative document edits, notifications. In all of these, the client is not the one who knows new data is available. The server is.
Why can't you just poll?
The naive fix: have the client ask the server "is there anything new?" every N seconds.
Client → Server: anything new?
Server → Client: no
[wait 2 seconds]
Client → Server: anything new?
Server → Client: no
[wait 2 seconds]
Client → Server: anything new?
Server → Client: yes, here's a new chat message
This works, technically. But consider what happens at scale:
If you have 100,000 users polling every 2 seconds, that's 50,000 requests per second hitting your servers — the vast majority returning "no." You're paying full HTTP overhead (headers, connection overhead, server-side processing) for 99% of requests that carry no useful information. The server is being hammered to say nothing.
And the latency is terrible. If something happens 1 millisecond after the client polled, it won't find out for up to 2 seconds. For a chat app this feels broken. For a live scoreboard it's embarrassing.
Polling is wasteful and slow. But it was the only option HTTP/1.1 offered.
What is long polling — and why is it still not enough?
Long polling is a clever hack to squeeze server-push behavior out of HTTP's request-response model.
Instead of the server responding immediately with "nothing new," it holds the connection open and responds only when there's actually something to say. From the server's perspective: receive request, park it, wait for an event, then respond. From the client's perspective: the request takes a long time (hence "long"), but when it completes, there's always real data in the response.
Client → Server: anything new? [server holds connection open]
[20 seconds pass]
Server → Client: yes, here's a new chat message [connection closes]
Client → Server: anything new? [immediately reconnects]
This is much better than regular polling — no wasted "nothing new" responses, and latency approaches zero (the server responds the instant there's data). It was the best real-time technique available for years, and many production systems used it.
But it has real costs:
The server must maintain a stalled HTTP connection for every waiting client. A thread or file descriptor held open per connection. At 100,000 concurrent users, that's 100,000 open connections sitting idle most of the time. This is manageable with async I/O but still adds significant server-side complexity.
Every time new data arrives, the connection closes and the client immediately reconnects — a new TCP handshake, new HTTP headers, new server-side state. On a chatty channel this reconnect churn is constant. And HTTP headers are sent on every request/response cycle — the same Cookie, User-Agent, Authorization repeated thousands of times per user.
Long polling is HTTP trying to be something it wasn't designed to be.
What does the problem actually need?
Think about what real-time communication actually requires:
- Either side can send a message at any time
- Low latency — a message sent should arrive in milliseconds, not seconds
- Low overhead — the cost of sending a small message should be small
- The connection stays alive — no reconnect dance for every message
HTTP's request-response model satisfies none of these at scale. What's needed is a persistent, full-duplex channel — a pipe that stays open and allows both sides to write to it at any time.
This is exactly what WebSockets provide.
How do WebSockets actually work?
WebSockets start as HTTP — the client sends a regular HTTP GET request with a special header:
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
The server agrees:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
After the 101 Switching Protocols response, the TCP connection is no longer HTTP. Both sides have agreed to speak a different protocol — the WebSocket protocol — over the same TCP connection. From this point:
- Either side can send a frame at any time
- Frames can carry text, binary data, or control signals (ping/pong for keepalive, close)
- The framing overhead is tiny — as few as 2 bytes of header for a small message, compared to hundreds of bytes of HTTP headers
The server can push a message to the client without the client asking. The client can send a message to the server without starting a new request. The connection stays open until either side explicitly closes it.
Why does starting with an HTTP upgrade make sense?
You might wonder: why not just open a raw TCP connection on a different port?
Because the web has infrastructure. Proxies, firewalls, load balancers — all of them understand HTTP and port 80/443. A raw TCP connection on a custom port would be blocked by most corporate firewalls and wouldn't pass through HTTP proxies.
By starting as HTTP and upgrading, WebSockets work through existing web infrastructure. Port 443 (HTTPS) is almost universally allowed, and the upgrade is transparent to intermediaries that don't understand WebSockets (they just pass the TCP bytes through).
What does WebSockets not solve?
WebSockets are full-duplex — both sides can send at any time. But not every use case needs that. Many real-time features are one-directional: the server pushes updates, and the client only listens. Live scores, price feeds, notifications, progress bars. For these, WebSockets is slightly more than you need — you're establishing bidirectional capability to use only one direction.
This is what Server-Sent Events (SSE) is for — the subject of the next question.
WebSockets also don't solve connection reliability. If the network drops, the WebSocket connection breaks and the client must reconnect and re-establish state. Applications usually build a reconnect-with-backoff loop and some form of state resync on top.
The mental model
HTTP (polling): client asks every N seconds → wasteful, high latency
Long polling: client holds connection open → better latency, but reconnect overhead
WebSocket: persistent full-duplex channel, tiny frame overhead, either side sends anytime
WebSocket is the right model when:
├── Server needs to push (chat, live data, notifications)
├── Client also needs to send frequently (collaborative editing, gaming)
└── Low latency and low overhead matter
How it works:
HTTP GET with "Upgrade: websocket"
→ Server responds 101 Switching Protocols
→ TCP connection is now a WebSocket channel
→ Both sides send frames freely until one sends Close
WebSockets exist because HTTP's request-response model is the wrong abstraction for communication where either party might have something to say at any moment.