Skip to content

WebSocket Rate Limiting — Complete Guide to Message Control

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Websocket Rate Limiting. We cover key concepts, practical examples, and best practices to help you master this topic.

WebSocket rate limiting controls message frequency per connection, preventing abuse and ensuring fair resource allocation across all connected clients in real-time applications.

What You'll Learn

  • Rate limiting strategies for WebSocket connections
  • Token bucket and Sliding Window algorithms
  • Implementing per-connection and per-user limits

Why It Matters

Unlike HTTP, WebSocket connections are persistent and bidirectional. A single client sending 10,000 messages per second can degrade service for all other connected clients.

Real-World Use

Durga Antivirus Pro WebSocket server enforces 60 messages per minute per connection using a sliding window algorithm. When exceeded, the server sends a rate limit warning and drops excess messages.

flowchart LR
    M["Incoming Message"] --> C["Check Rate Limit"]
    C -->|"Under Limit"| P["Process"]
    C -->|"Over Limit"| W["Warning"]
    W -->|"Exceeded"| D["Drop or Disconnect"]
    style C fill:#dbeafe,stroke:#2563eb

Code Examples

// Sliding window rate limiter per connection
const WebSocket = require('ws');

const rateLimiters = new Map();

function checkRateLimit(ws, maxMessages = 60, windowMs = 60000) {
  if (!rateLimiters.has(ws)) {
    rateLimiters.set(ws, { timestamps: [], warned: false });
  }

  const limiter = rateLimiters.get(ws);
  const now = Date.now();

  // Remove timestamps outside the window
  limiter.timestamps = limiter.timestamps.filter(t => now - t < windowMs);

  if (limiter.timestamps.length >= maxMessages) {
    if (!limiter.warned) {
      ws.send(JSON.stringify({ type: 'rate_limit', message: 'Slow down' }));
      limiter.warned = true;
    }
    return false;
  }

  limiter.timestamps.push(now);
  limiter.warned = false;
  return true;
}

const server = new WebSocket.Server({ port: 8080 });

server.on('connection', (ws) => {
  ws.on('message', (data) => {
    if (!checkRateLimit(ws)) {
      return; // Drop excess messages
    }
    handleMessage(ws, data);
  });

  ws.on('close', () => {
    rateLimiters.delete(ws); // Clean up on disconnect
  });
});

Expected output: Clients exceeding 60 messages/minute receive a rate limit warning; excess messages are dropped.

# Token bucket rate limiter for WebSocket
import asyncio
import time

class TokenBucket:
    def __init__(self, rate=10, burst=20):
        self.rate = rate          # Tokens per second
        self.burst = burst        # Max accumulated tokens
        self.tokens = burst
        self.last_refill = time.time()

    def refill(self):
        now = time.time()
        elapsed = now - self.last_refill
        self.tokens = min(self.burst, self.tokens + elapsed * self.rate)
        self.last_refill = now

    def consume(self):
        self.refill()
        if self.tokens >= 1:
            self.tokens -= 1
            return True
        return False

class RateLimitedWebSocket:
    def __init__(self, websocket, rate=10, burst=20):
        self.ws = websocket
        self.bucket = TokenBucket(rate, burst)

    async def handle_message(self, message):
        if not self.bucket.consume():
            await self.ws.send(json.dumps({
                'type': 'rate_limit',
                'message': 'Message rate exceeded'
            }))
            return
        await process_message(self.ws, message)

Expected output: Token bucket allows bursts up to 20 messages then limits to 10 messages/second sustained.

// Per-user rate limiting across multiple connections
const rateLimits = new Map(); // userId -> { tokens, lastRefill }

function checkUserRateLimit(userId, maxMessages = 100, windowMs = 60000) {
  if (!rateLimits.has(userId)) {
    rateLimits.set(userId, { count: 0, startTime: Date.now() });
  }

  const limit = rateLimits.get(userId);
  if (Date.now() - limit.startTime > windowMs) {
    limit.count = 0;
    limit.startTime = Date.now();
  }

  limit.count++;
  return limit.count <= maxMessages;
}

// Shared limit across all connections for the same user
server.on('connection', (ws) => {
  ws.on('message', () => {
    if (!checkUserRateLimit(ws.userId)) {
      ws.send(JSON.stringify({ type: 'rate_limit', message: 'Global rate limit' }));
      return;
    }
  });
});

Expected output: User rate limit aggregates across all their connections; prevents circumvention by opening multiple WebSockets.

Common Mistakes

1. No Rate Limiting at All

WebSocket connections without rate limiting allow a single client to flood the server, degrading service for all.

2. Per-Connection Limits Only

Users can open multiple connections to bypass per-connection limits. Implement per-user limits too.

3. Disconnecting Immediately on First Violation

A single burst should not disconnect. Send warnings, then progressively stricter measures.

4. Not Cleaning Up Limiters

Rate limiter state for disconnected connections accumulates memory. Clean up on close.

5. Applying Limits to System Messages

Server-generated heartbeats and acknowledgments should not count against client rate limits.

Practice Questions

  1. Why is rate limiting important for WebSocket connections?
  2. What is the difference between token bucket and sliding window algorithms?
  3. Why should you implement both per-connection and per-user rate limiting?
  4. How do you handle a client that consistently exceeds rate limits?
  5. Why should you not disconnect on the first rate limit violation?

Answers:

  1. Unlike HTTP, WebSocket is persistent; a single client can send unlimited messages, degrading service.
  2. Token bucket allows bursts up to a capacity; sliding window enforces a hard limit over a precise time window.
  3. Per-connection limits are bypassed by opening multiple connections; per-user limits prevent this.
  4. Send warnings, progressively reduce rate, and only disconnect after repeated violations.
  5. Legitimate clients may burst legitimately (e.g., re-sync after reconnect). Warnings give them a chance to adjust.

Challenge: Implement a multi-tier WebSocket rate limiter with: per-connection sliding window (60 msg/min), per-user token bucket (200 msg/min across all connections), progressive warnings (warn, reduce rate, disconnect after 3 violations in 5 minutes).

FAQ

What is a good default rate limit for WebSocket messages?

: 30-60 messages per minute for chat, 100-300 for real-time data feeds, depending on use case.

Should rate limiting apply to all message types equally?

: No, allow higher limits for low-impact messages (heartbeats) and stricter limits for expensive operations (broadcasts).

How do you handle rate limiting for broadcast messages from server?

: Server broadcasts do not count against client limits. Only client-to-server messages are rate limited.

What headers or frames indicate rate limiting?

: Send a JSON message with type: rate_limit or use WebSocket close code 1008 (Policy Violation).

Can rate limiting be implemented at the load balancer?

: Yes, but load balancer rate limits apply per connection; application-level limits allow per-user aggregation.

Mini Project

Build a WebSocket rate limiter with: per-connection sliding window (60 msg/min with 10 msg/min burst allowed), per-user token bucket (200 msg/min across all connections), progressive warnings (warn at 80%, drop at 100%, disconnect at 150% of limit). Include cleanup on disconnect.

What's Next

Learn about WebSocket authentication for identifying users, or explore WebSocket compression for bandwidth optimization.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro