On this page
WebSocket and SSE
The HTTP request-response model is not suitable for real-time scenarios—WebSocket upgrades to a full-duplex channel, while SSE maintains simplicity with a unidirectional stream. The choice depends on "whether the server needs to push proactively" and "whether binary frames are required."
Overview
WebSocket (RFC 6455, 2011) creates a persistent bidirectional TCP channel on top of the HTTP handshake—browsers and servers can send messages to each other at any time. This solves the "request-response" unidirectional limitation of HTTP, enabling real-time applications such as chat, games, live streaming bullet comments, and Grafana Live. SSE (Server-Sent Events) is a simpler unidirectional alternative: the server pushes an event stream to the client, based on plain HTTP, with better firewall compatibility. Both have heartbeat mechanisms (ping/pong vs. SSE's automatic reconnection) to keep long-lived connections alive in NAT/proxy environments.
WebSocket Upgrade
Client → Server: GET /ws HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== ← random, base64
Sec-WebSocket-Version: 13
Sec-WebSocket-Protocol: chat ← optional subprotocol
Server → Client: HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= ← SHA1(Key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
Sec-WebSocket-Protocol: chat
→ Handshake complete → TCP connection upgraded to bidirectional WebSocket
WebSocket Frame
0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Masking-key (if MASK set, 4 bytes) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Payload Data (variable, masked if from client) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
FIN(1): 1 = this is the final frame of the message
opcode(4): 0=continuation, 1=text(UTF-8), 2=binary, 8=close, 9=ping, 10=pong
MASK(1): client→server must mask, server→client must not mask
Masking (client → server):
1. Client: randomly generate a 4-byte masking key
2. For each byte of the payload: payload[i] = original[i] XOR key[i % 4]
3. Purpose: prevent cache poisoning by intermediate proxies (an attacker forces the browser to send crafted WS payloads
→ proxy misinterprets them as HTTP responses → corrupts cache)
Close frame (opcode=8):
[status code (2 bytes, big-endian)][reason (UTF-8, optional)]
1000: normal closure
1001: going away (server shutdown, page navigated away)
Ping/Pong (Heartbeat)
Ping frame (opcode=9): optional payload (application-specific)
Pong frame (opcode=10): must copy the ping's payload
→ Application-layer heartbeat (distinct from TCP keepalive)
→ Detection: dead connections (even if TCP is still alive, the remote app may be hung)
Proxy/server timeout: nginx proxy_read_timeout applies to WebSocket →
Without ping/pong, the connection may be timed out and disconnected by the proxy
SSE (Server-Sent Events)
Unidirectional (server → client) stream, based on HTTP chunked encoding:
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
id: 42 ← last event ID (auto-reconnect from this ID)
event: alert ← optional event type (otherwise defaults to "message")
data: {"status":"firing"} ← event data
data: multi-line data
data: second line
← empty line → dispatch event
Client API: EventSource (built-in HTML5):
const es = new EventSource("/events");
es.onmessage = (e) => { console.log(e.data); };
es.addEventListener("alert", (e) => { ... });
WebSocket vs SSE
| WebSocket | SSE | |
|---|---|---|
| Direction | Bidirectional | Server → Client |
| Protocol | WebSocket (upgrade) | HTTP chunked |
| Binary data | Native (opcode=2) | Requires base64 encoding |
| Firewall compatibility | Poor (some proxies do not support upgrade) | Good (plain HTTP) |
| Automatic reconnection | Manual | Automatic via EventSource (since last-event-id) |
| Suitable for | Chat, games, real-time collaboration | Notifications, feeds, real-time data push |
References
- RFC: 6455
- SSE: html.spec.whatwg.org/multipage/server-sent-events.html
- WS Testing: websocat (CLI WebSocket client)
Keywords: WebSocket, upgrade, frame, masking, ping/pong, SSE, EventSource, heartbeat