# Scaling and Securing WebSockets

Canonical: https://gaboesquivel.com/blog/2025-05-scaling-securing-websockets

**Published:** 2025-05-16
**Summary:** How session affinity, TLS, authentication, and Redis pub/sub keep WebSocket backends reliable across multiple nodes.
**Categories:** Engineering, Backend
**Technologies:** NodeJS

WebSockets carry the real-time part of products from trading engines to AI assistants, and the prototype that works on one server gets harder in production once uptime, load balancing, and secure communication matter. This is how I approach scaling WebSocket backends in high-availability environments without giving up security or developer experience.

## Persistent connections behind a load balancer

A WebSocket holds a long-lived TCP connection, which breaks stateless load balancing, so the load balancer needs session affinity, also called sticky sessions.

**NGINX**, **HAProxy**, and most managed load balancers, including [AWS](https://gaboesquivel.com/tech/aws) ALB, support sticky sessions based on cookies or IP hashing, which keeps each client routed to the same backend node for the life of its connection.

## TLS with wss:// only

Production WebSockets should never run over `ws://`, only over TLS-encrypted `wss://`.

You can terminate TLS at the load balancer, such as Cloudflare or NGINX, and forward traffic to internal nodes over plain TCP, as long as you take care of three things:

* Use valid certificates, from Let's Encrypt or a managed provider.
* Redirect every insecure connection.
* Set CORS policies if the frontend is hosted separately.

## Authentication at the handshake

WebSocket connections don't carry HTTP headers after the handshake, so authentication has to happen at connection time. My usual setup has three parts:

* JWT-based auth when the connection opens.
* Validation through the query string or an initial message payload.
* Periodic token revalidation for long sessions.

I pair that with **[Redis](https://gaboesquivel.com/tech/redis)** or **Kafka** to coordinate presence, rate limits, and bans across nodes.

## Redis pub/sub for multi-node messaging

In a multi-instance setup, the WebSocket servers need to talk to each other, because a user connected to node A and a friend connected to node B can only chat if the message crosses nodes. Publishing to a central broker solves that, and I default to **Redis Pub/Sub** for its speed and small operational footprint, moving to Kafka when the messages need durability or streaming.

![WebSocket clients connected through TLS load balancer to nodes sharing Redis pub/sub](https://gaboesquivel.com/images/diagrams/2025-05-scaling-securing-websockets.svg)

Each client maintains a connection to its server node; pub/sub distributes messages across nodes.

## Bun WebSocket as a performance baseline

If you build with [Bun](https://bun.sh/), you get its native WebSocket implementation, which handles thousands of concurrent connections with low memory overhead. With it you can:

* Launch a standalone server with Bun's `serve()` and native `upgrade` handling.
* Integrate Redis Pub/Sub or a shared memory queue.
* Run it close to users on edge infrastructure.

That makes it a good fit for edge-native, low-latency real-time apps.

## Rate limiting, monitoring, and DDoS protection

A WebSocket server should never face the open internet without protection in front of it:

* **Cloudflare** or another WAF.
* IP-based rate limiting at the load balancer.
* Application-level limits on messages per second per user.
* Logging and metrics through Prometheus, Datadog, or Sentry.

## Production checklist

Before a WebSocket backend goes live, I check each of these:

* Sticky sessions at the load balancer, by cookie or IP hash.
* TLS with `wss://` only.
* Token-based auth at the handshake.
* Redis for pub/sub or shared state.
* Bun WebSocket where low-latency edge performance matters.
* Rate limiting and monitoring.
* Health checks and auto-scaling.

The same architecture holds for [Web3](https://gaboesquivel.com/web3) trading apps, multiplayer interfaces, and streamed [AI](https://gaboesquivel.com/ai) results, with the load balancer, the broker, and the auth handshake as the three pieces I settle before writing any message handlers.