WebSockets and Server-Sent Events Explained
HTTP is request-response. The client asks, the server answers, conversation over. But chat apps, live dashboards, multiplayer games, and stock tickers need the server to PUSH updates to the client without being asked. WebSockets and Server-Sent Events (SSE) are the two main solutions.
The problem with plain HTTP
“Polling” — client asks every few seconds “anything new?” — wastes bandwidth and is slow. “Long polling” — client asks and server holds the connection open until something happens — works but has its own complications. WebSockets and SSE solve this properly.
WebSockets — bidirectional, persistent
WebSocket starts as an HTTP request that “upgrades” to a different protocol. After the upgrade, the connection becomes a bidirectional message channel — both sides can send anything, anytime, until one side closes it.
The handshake
Client request:
GET /chat 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=
[connection now uses WebSocket framing — both can send anytime]
Browser code
const ws = new WebSocket('wss://example.com/chat');
ws.onopen = () => {
ws.send(JSON.stringify({type: 'join', room: 'general'}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log('Received:', msg);
};
ws.onclose = () => console.log('Connection closed');
ws.onerror = (e) => console.error('Error:', e);
// Send any time
ws.send(JSON.stringify({type: 'message', text: 'Hello'}));
When to use WebSockets
- Chat applications
- Multiplayer games
- Collaborative editing (Google Docs-style)
- Live dashboards with user interaction
- Anything bidirectional and frequent
Server-Sent Events (SSE) — one-way, simpler
SSE keeps an HTTP connection open and lets the SERVER push events to the client. The client cannot send messages back over the same channel — it would use a separate HTTP request for that.
The wire format (just plain text)
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
event: message
data: {"text": "Hello"}
event: notification
data: {"id": 123, "kind": "alert"}
[connection stays open, more events appended]
Browser code
const es = new EventSource('/stream');
es.onmessage = (e) => {
console.log('Default event:', e.data);
};
es.addEventListener('notification', (e) => {
const data = JSON.parse(e.data);
console.log('Notification:', data);
});
es.onerror = () => console.log('Connection lost (auto-reconnects)');
When to use SSE
- Live news feeds
- Stock tickers
- Server-side job progress
- Read-only live dashboards
- Any push-only stream where bidirectional isn’t needed
WebSocket vs SSE comparison
| Feature | WebSocket | SSE |
|---|---|---|
| Direction | Bidirectional | Server → Client only |
| Protocol | Custom (ws://, wss://) | Plain HTTP |
| Auto-reconnect | Manual | Built-in |
| Binary support | Yes | Text only |
| Through proxies | Often blocked | Works (it’s HTTP) |
| Browser support | Universal | Universal except old IE |
| Server complexity | Higher | Lower (just keep HTTP open) |
Modern alternative: WebTransport
Built on HTTP/3 / QUIC. Solves WebSocket’s head-of-line blocking by using QUIC’s per-stream loss recovery. Adoption is growing but still partial. Worth knowing about for new projects.
What to learn next
That covers HTTP and the application layer. Next big section: routing — how packets find their way across networks. Up next.