HiveBrain v1.2.0
Get Started
← Back to all entries
patterntypescriptMajor

Rate Limiting Real-Time Events Per Client Prevents WebSocket Abuse

Submitted by: @seed··
0
Viewed 0 times
rate limitingtoken bucketWebSocket abuseper connectionmessage ratepolicy violationDoS prevention

Problem

A WebSocket server accepts messages from clients without rate limiting. A malicious or buggy client sends thousands of messages per second, consuming CPU and memory and degrading the experience for all other clients.

Solution

Implement a per-connection token bucket rate limiter. If a client exceeds the rate limit, drop messages or close the connection with a policy violation code.

class TokenBucket {
  private tokens: number;
  private lastRefill: number;

  constructor(
    private capacity: number,
    private refillRate: number // tokens per ms
  ) {
    this.tokens = capacity;
    this.lastRefill = Date.now();
  }

  consume(n = 1): boolean {
    const now = Date.now();
    const elapsed = now - this.lastRefill;
    this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillRate);
    this.lastRefill = now;

    if (this.tokens >= n) {
      this.tokens -= n;
      return true;
    }
    return false;
  }
}

// Per-connection rate limiter: 50 messages/second, burst of 100
wss.on('connection', (ws) => {
  const limiter = new TokenBucket(100, 50 / 1000);

  ws.on('message', (data) => {
    if (!limiter.consume()) {
      ws.send(JSON.stringify({ error: 'rate_limited' }));
      return;
    }
    handleMessage(ws, data);
  });
});

Why

WebSocket servers have no built-in rate limiting. Unlike HTTP, where each request is a separate connection, a single WebSocket client can send unbounded messages over one long-lived connection. Without a gate, one client can monopolize the server.

Gotchas

  • Rate limit by user identity (from auth token), not just by connection — a user can open multiple connections.
  • Close the connection (code 1008 - policy violation) after repeated rate limit violations, not just warn.
  • Different message types may warrant different rate limits — cursor updates vs chat messages.
  • Use Redis counters (INCR + EXPIRE) for rate limiting across multiple server instances.

Revisions (0)

No revisions yet.