WebSockets are a protocol that upgrades an HTTP connection to a persistent, full-duplex TCP channel. Once established, both the browser and server can send messages at any time without the overhead of new HTTP handshakes. This is the foundation of collaborative editing, live dashboards, multiplayer games, chat applications, and real-time financial data. In 2026, the WebSocket protocol itself is mature and stable — what varies is how teams handle the hard parts: authentication, reconnection, and scaling.
What changed in 2026
- Cloudflare Durable Objects became a popular WebSocket backend. They provide a single, stateful JavaScript object per connection group at the edge, eliminating the need for Redis Pub/Sub in many real-time workloads.
- HTTP/3 (QUIC) competes for some WebSocket use-cases. QUIC's multiplexed streams eliminate head-of-line blocking, making HTTP/3 suitable for some real-time patterns that previously needed WebSockets.
- Server-Sent Events (SSE) regained attention as HTTP/2 made SSE more practical (multiple SSE streams over one connection) and AI chat UIs (streaming LLM output) drove massive SSE adoption.
- Socket.IO 4.x became thinner — the library now defaults to pure WebSocket with automatic fallback, reducing bundle size.
How WebSockets work
Client Server
| |
|---HTTP GET /ws + Upgrade----->|
|<--101 Switching Protocols-----|
| |
|<======= WS Frame (text) ======| ← server push
|======= WS Frame (binary) ===>| ← client message
| |
|---WS Close Frame------------->|
The 101 Switching Protocols response completes the handshake. After that, both sides communicate using WebSocket frames — not HTTP.
WebSockets vs Server-Sent Events vs Long Polling
| Feature |
WebSockets |
SSE |
Long Polling |
| Direction |
Bidirectional |
Server → Client only |
Server → Client |
| Protocol |
WS/WSS (TCP) |
HTTP/HTTPS |
HTTP/HTTPS |
| Browser support |
All |
All (IE 11 needs polyfill) |
All |
| Reconnection |
Manual |
Built-in (EventSource) |
Manual |
| Load balancer friendly |
Requires sticky sessions |
Yes |
Yes |
| Good for |
Chat, games, collaboration |
Notifications, feeds, AI streaming |
Legacy fallback |
Rule of thumb: if you only need the server to push updates (notifications, live feeds, LLM token streaming), use SSE — it is simpler and load balancer-friendly. Use WebSockets when the client also sends frequent messages.
Minimal WebSocket server (Node.js)
import { WebSocketServer } from "ws";
const wss = new WebSocketServer({ port: 8080 });
wss.on("connection", (ws, req) => {
const userId = authenticate(req); // validate JWT from query param or cookie
ws.on("message", (data) => {
const message = JSON.parse(data.toString());
// Handle message, broadcast to other clients, etc.
broadcast(message, ws);
});
ws.on("close", () => cleanup(userId));
ws.on("error", (err) => console.error(err));
});
Scaling WebSockets
A single server holds all WebSocket connections in memory. When you scale to multiple servers, a message sent by Client A (connected to Server 1) destined for Client B (connected to Server 2) won't arrive — unless you add a pub/sub layer.
Sticky sessions approach
Configure your load balancer (Nginx, AWS ALB) to route each client to the same server. Simple, but limits horizontal scaling and breaks on server restart.
Redis Pub/Sub approach
import { createClient } from "redis";
const publisher = createClient();
const subscriber = createClient();
await subscriber.subscribe("chat:room:42", (message) => {
// Send to all local WebSocket clients in room 42
broadcastLocal(message);
});
// When any server receives a client message, publish it
async function onClientMessage(roomId: string, message: string) {
await publisher.publish(`chat:${roomId}`, message);
}
Every server subscribes to relevant channels. When a message arrives, it broadcasts locally to connected clients.
Managed real-time platforms
| Platform |
Model |
Pricing |
Best for |
| Ably |
Managed pub/sub + presence |
Per message |
Production chat, collab |
| Pusher |
Managed channels |
Per connection/message |
Simpler apps |
| Liveblocks |
Collaborative state |
Per room/month |
Figma-style collaboration |
| Cloudflare Durable Objects |
Edge stateful objects |
Per request/GB |
Edge WebSockets |
| Soketi |
Self-hosted Pusher API |
Infrastructure cost |
Budget-conscious |
How to pick
- Prototyping or low traffic? Direct WebSocket server without Redis is fine.
- Chat, notifications, live feeds (server → client)? Use SSE first — simpler, load balancer-friendly.
- Bidirectional with scaling needs? WebSocket + Redis Pub/Sub, or Socket.IO.
- Collaborative editing (shared state, presence)? Liveblocks or a CRDT library (Yjs) with a WebSocket backend.
- Edge / low-latency global? Cloudflare Durable Objects.
- No infra ops budget? Ably or Pusher managed services.
Common mistakes
Not handling reconnection. Networks drop. Clients must implement exponential backoff and session resumption. Socket.IO handles this automatically; raw WebSocket requires manual implementation.
No heartbeats (ping/pong). Load balancers and firewalls close idle TCP connections after ~60–90 seconds. Send a ping frame every 30 seconds.
// Keep-alive ping every 30 seconds
const interval = setInterval(() => {
if (ws.readyState === ws.OPEN) ws.ping();
}, 30_000);
ws.on("close", () => clearInterval(interval));
Blocking the event loop in the message handler. WebSocket message handlers run on the event loop. Heavy computation must be offloaded to a worker thread or background job queue.
Not rate limiting connections and messages. Without rate limiting, a single client can open thousands of connections or flood message handlers.
What to skip
- Raw WebSockets for production without a framework — use Socket.IO or
ws with proper abstractions for auth, reconnection, and namespacing.
- WebSockets for REST-like request/response — the overhead of keeping a connection alive is not worth it for infrequent request/reply patterns.
- Self-managed Pub/Sub at scale — above ~10k concurrent connections with complex routing, managed platforms pay for themselves in engineering time saved.
FAQ
Are WebSockets secure?
Use WSS (WebSocket Secure) — the WebSocket protocol over TLS. Same as HTTPS vs HTTP. Never use plain WS in production.
How many WebSocket connections can a server handle?
A well-tuned Node.js server handles ~50k–100k concurrent connections. Go-based servers (Gorilla WebSocket) and Rust handle more, limited by file descriptor limits and memory (~2–3 KB per connection).
Do WebSockets work through proxies?
Depends on the proxy. Nginx supports WebSocket proxying with proxy_http_version 1.1 and Upgrade/Connection headers. Some corporate firewalls block WebSocket upgrades — SSE as fallback helps.
What is the difference between Socket.IO and raw WebSockets?
Socket.IO adds namespaces, rooms, acknowledgments, automatic reconnection, and a polling fallback on top of WebSockets. It is heavier (~45 KB) but solves many edge cases. For simple use-cases, raw ws is sufficient.
Where to go next