Fixed window vs sliding window rate limiting algorithms
fixed window algorithm, sliding window log algorithm, sliding window counter, boundary spike problem, memory trade-offs, algorithm selection
Fixed window vs sliding window rate limiting algorithms
Fixed Window
Divide time into fixed intervals such as each minute. Count requests per interval and reset at the boundary. Simple and memory-efficient:
const key = `ratelimit:${userId}:${Math.floor(Date.now() / 60000)}`;
const count = await redis.incr(key);
await redis.expire(key, 60);
if (count > 100) return res.status(429).json({ error: 'Rate limit exceeded' });Problem: a client sends 100 requests at 00:59 and 100 more at 01:01 — 200 requests in 2 seconds across two windows, both under the limit. This boundary spike can overwhelm your backend.
Sliding Window Log
Store a timestamp for each request in a sorted set. Remove timestamps older than the window, then check count. Accurate but memory-intensive:
const now = Date.now();
const windowStart = now - 60000;
await redis.zremrangebyscore(key, 0, windowStart);
const count = await redis.zcard(key);
if (count >= 100) return res.status(429).end();
await redis.zadd(key, now, now);
await redis.expire(key, 60);Sliding Window Counter
Hybrid approach: use two fixed-window counters (current and previous minute) weighted by how far into the current window you are. Approximates sliding window at fixed-window memory cost. Used by Cloudflare at scale. For most applications, fixed window is sufficient — only move to sliding window if boundary spikes are measurable in your production metrics.
