Real-Time Communication: WebSockets, SSE & HTTP/3 at Production Scale
WebSockets, SSE, and HTTP/3 each solve a different real-time problem — full-duplex state sync, efficient server push, and transport-layer resilience — and using the wrong primitive creates horizontal scaling problems that appear only under load. This article builds WebSocket server architecture from connection lifecycle through Redis PubSub fan-out, contrasts SSE for one-way push, and explains why HTTP/3's QUIC eliminates the head-of-line blocking that makes TCP unsuitable for multiplexed real-time APIs.
API Architecture & System Resilience
Real-Time Communication: WebSockets, SSE & HTTP/3 at Production Scale
Senior engineers don't just wire services together — they design the boundary: the contract, the trust model, the failure envelope, and the signal pipeline that proves it's working. Real-time communication is where the failure envelope is most exposed: a WebSocket server that works perfectly under test will exhaust file descriptors when ten thousand users connect simultaneously because no one counted the connections. An SSE endpoint that streams updates correctly on a single node silently breaks when a second node is added because the events originate on whichever node processed the write, not the node holding the client's connection. HTTP/3 eliminates a class of head-of-line blocking failures that engineers have been working around for twenty years — and most teams do not know when to adopt it. This article derives each primitive from the scaling constraint it solves.
Series positioning: This is Part 4 of the API Architecture & System Resilience series. It builds on the API Gateway from Part 2. The GraphQL-specific WebSocket and SSE transport patterns are covered in the separate GraphQL Backend & API Design series — this article focuses on general-purpose server-side real-time architecture.
1. WebSocket Architecture: Connection Lifecycle & Memory Model
1.1 The Upgrade Handshake
After the 101 Switching Protocols response, the HTTP connection is replaced by a bidirectional TCP stream. No more HTTP request/response cycle — both ends can write at any time.
1.2 Per-Connection Memory Planning
TCP half-open connections — where the client disconnects without sending a FIN (e.g., a mobile device losing signal) — remain in the server's connection table consuming a file descriptor indefinitely unless the server detects them via application-level heartbeat. The ping/pong mechanism is mandatory for production WebSocket servers.
2. Horizontal Scaling: From Sticky Sessions to Redis PubSub
2.1 Why Sticky Sessions Fail Under Load
2.2 Redis PubSub Adapter

3. STOMP over WebSocket
STOMP (Simple Text Oriented Messaging Protocol) adds a message-level protocol on top of raw WebSocket frames — subscription channels, acknowledgment semantics, and receipt confirmation.
STOMP is the correct choice when clients need:
- Multiple logical channels over a single WebSocket connection (e.g.,
/user/orders,/user/notifications,/topic/system-announcements) - Acknowledgment-based delivery — clients acknowledge receipt; server retransmits unacknowledged frames
- Message receipts — clients can request a receipt for a specific send operation
4. Server-Sent Events (SSE): Efficient One-Way Push
4.1 Client-Side Reconnect via Last-Event-ID
5. WebSocket vs SSE Decision Framework
| Criterion | WebSocket | SSE |
|---|---|---|
| Communication direction | Full-duplex (both ends can send at any time) | Server → client only |
| Protocol overhead | Custom frame framing overhead | Pure HTTP — no extra protocol |
| Proxy compatibility | Requires proxy WebSocket support | Works through any HTTP proxy |
| Load balancer | Requires sticky sessions or Redis adapter | Stateless — any instance handles it |
| Browser reconnect | Manual (must implement reconnect logic) | Automatic (EventSource spec) |
| Server implementation | Complex (upgrade handshake, ping/pong, frame framing) | Simple (chunked HTTP response) |
| Use when | Chat, collaborative editing, gaming, bidirectional control | Notifications, live feeds, dashboards, progress streaming |
If your real-time use case is purely server-to-client (notifications, live data feeds, progress updates), SSE is strictly superior to WebSockets: simpler server implementation, no upgrade handshake, automatic reconnection, and full HTTP proxy compatibility. WebSockets are only necessary when the client must also send data to the server over the same persistent connection.
6. HTTP/3 & QUIC: Eliminating Head-of-Line Blocking
6.1 TCP Head-of-Line Blocking in HTTP/2
6.2 QUIC: Independent Streams Over UDP
6.3 0-RTT Connection Establishment
0-RTT data in QUIC is replay-vulnerable by design — an attacker who captures a 0-RTT packet can replay it. Never use 0-RTT for non-idempotent requests (POST, DELETE). It is appropriate for GET requests and safe reads only. Most QUIC implementations require explicit opt-in for 0-RTT on per-route basis.
6.4 QUIC Connection Migration

6.5 When HTTP/3 Matters vs When It Is Premature Optimization
| Scenario | HTTP/3 Impact |
|---|---|
| Mobile APIs on lossy networks | High — packet loss stalls TCP; QUIC streams are independent |
| High-multiplex APIs (many parallel requests) | High — eliminates HOL blocking across all concurrent streams |
| Video streaming / media delivery | High — connection migration survives handoffs |
| Datacenter-only APIs (low packet loss) | Low — TCP HOL blocking rarely triggers; QUIC overhead exceeds benefit |
| Simple request/response APIs (< 5 concurrent requests) | Low — multiplexing advantage not realized |
| APIs behind CDN | Medium — CDN terminates HTTP/3; origin is still HTTP/2 |
Summary
| Primitive | Rule |
|---|---|
| WebSocket connections | Count file descriptors; implement heartbeat (ping/pong); hard-cap MAX_CONNECTIONS |
| WebSocket horizontal scaling | Redis PubSub adapter — never sticky sessions; sticky fails silently on node death |
| STOMP over WebSocket | Use when clients need multiple logical channels or acknowledgment semantics |
| SSE | Prefer over WebSocket for server-push; simpler, proxy-transparent, auto-reconnect |
| SSE reconnect | Return Last-Event-ID in stream; server replays missed events on reconnect |
| HTTP/3 adoption | Prioritize for mobile APIs, lossy networks, high-multiplexing; skip for datacenter-only |
| 0-RTT | Never for non-idempotent requests — replay attack surface |
| QUIC migration | Enables seamless WiFi→LTE transitions without reconnect for mobile clients |
What's Next
In Part 5, we turn to the identity layer — Part 5: JWT, OIDC & RBAC builds the complete authentication architecture: how JWTs are verified statlessly at microservice boundaries, how OIDC federates identity for SSO, and how RBAC role claims flow from the gateway to every downstream service without duplicating authorization logic.
This article was developed with AI-assisted deep search, specification cross-referencing, and technical research synthesis.