Skip to content

Fixed Window Rate Limiting — Complete Implementation Guide

DodaTech Updated 2026-06-28 5 min read

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

Fixed window rate limiting divides time into discrete windows and allows a maximum number of requests per window, offering the simplest implementation at the cost of potential boundary spikes.

What You'll Learn

By the end of this tutorial, you will implement fixed window rate limiting, understand its boundary condition problem, and know when it is an appropriate choice.

Why It Matters

Fixed window is the default algorithm in many rate limiting libraries because of its simplicity and low memory requirements. DodaTech uses it for non-critical rate limits where slight inaccuracy is acceptable.

Real-World Use

DodaTech's static asset server uses fixed window rate limiting for CDN bandwidth management, where slight boundary spikes do not affect the Caching layer.

Fixed Window Learning Path

flowchart LR
  A[Leaky Bucket] --> B[Fixed Window]
  B --> C[Implementation]
  C --> D[Boundary Problem]
  B --> E{You Are Here}
  style E fill:#f90,color:#fff
}

## How Fixed Window Works

Time is divided into windows of equal duration (e.g., 1 minute). Each window has a counter. When the counter reaches the limit, requests are blocked until the next window.

```JavaScript
const Express = require("express");
const app = express();

const counters = new Map();

function fixedWindowLimiter(req, res, next) {
  const key = req.ip;
  const windowMs = 60000;
  const maxRequests = 10;
  const windowId = Math.floor(Date.now() / windowMs);
  const counterKey = `${key}:${windowId}`;

  const count = (counters.get(counterKey) || 0) + 1;
  counters.set(counterKey, count);

  if (count > maxRequests) {
    return res.status(429).json({ error: "Too many requests" });
  }

  next();
}

app.use(fixedWindowLimiter);
app.get("/", (req, res) => res.json({ ok: true }));
app.listen(3000);

Expected behavior: 10 requests are allowed per minute. At the next minute boundary, the counter resets. Cleanup of old counters is needed to prevent memory growth.

The Boundary Problem

The boundary problem occurs when a client makes requests near the end of one window and the beginning of the next, effectively doubling the allowed rate.

// Visualization of the boundary problem
// Window 1: 00:00 - 00:59 - requests: 10
// Window 2: 01:00 - 01:59 - requests: 10
// A client making 10 requests at 00:59 and 10 at 01:00
// has 20 requests in 2 seconds, bypassing the rate limit intent.

Fixed Window with Redis

For distributed deployments, Redis provides atomic counter operations with automatic TTL-based cleanup.

const Redis = require("ioredis");
const redis = new Redis();

async function redisFixedWindow(req, res, next) {
  const key = `fw:${req.ip}:${Math.floor(Date.now() / 60000)}`;
  const maxRequests = 10;

  const count = await redis.incr(key);

  if (count === 1) {
    await redis.expire(key, 120);
  }

  if (count > maxRequests) {
    return res.status(429).json({ error: "Rate limit exceeded" });
  }

  next();
}

Expected behavior: Atomic increment ensures accurate counting across servers. TTL of 120 seconds (2 windows) ensures automatic cleanup.

Improving Fixed Window

A simple improvement halves the boundary problem by using overlapping windows and weighting counts from the previous window.

function improvedFixedWindow(req, res, next) {
  const key = req.ip;
  const windowMs = 60000;
  const maxRequests = 10;
  const now = Date.now();

  const currentWindow = Math.floor(now / windowMs);
  const previousWindow = currentWindow - 1;

  const currentKey = `${key}:${currentWindow}`;
  const previousKey = `${key}:${previousWindow}`;

  const currentCount = (counters.get(currentKey) || 0) + 1;
  const previousCount = counters.get(previousKey) || 0;

  const elapsed = now - (currentWindow * windowMs);
  const weight = 1 - (elapsed / windowMs);
  const weightedCount = currentCount + (previousCount * weight);

  counters.set(currentKey, currentCount);

  if (weightedCount > maxRequests) {
    return res.status(429).json({ error: "Too many requests" });
  }

  next();
}

Common Mistakes

  1. Not cleaning up old counters — Fixed window stores grow forever. Use TTL-based stores or periodic cleanup.

  2. Ignoring clock skew — Servers with different clocks calculate different window IDs. Use a centralized time source.

  3. Using fixed window for authentication endpoints — Login endpoints need precise limits. Fixed window's boundary problem allows brute force bursts.

  4. Large window sizes causing memory issues — Hour-long windows with high traffic store many counter keys. Use shorter windows or sliding algorithms.

  5. Not handling counter overflow — Counters stored as 32-bit integers can overflow. Use larger integer types or reset periodically.

Practice Questions

  1. What happens at the boundary between two fixed windows? The counter resets, allowing a client to make requests at the end of one window and the start of the next, potentially doubling the rate.

  2. How do you prevent memory growth in fixed window counters? Use TTL-based stores (Redis) or periodically purge counters older than one window.

  3. When is fixed window an acceptable choice? For non-critical rate limits, for general API rate limiting where slight inaccuracy is acceptable, or when simplicity matters more than precision.

  4. Challenge: Build a fixed window limiter that logs warnings when approaching the limit.

function warningLimiter(req, res, next) {
  const count = getCurrentCount(req.ip);
  const limit = 100;
  if (count > limit * 0.8) {
    console.warn(`Client ${req.ip} at ${count}/${limit} requests`);
  }
  if (count >= limit) return res.status(429).json({ error: "Limit exceeded" });
  next();
}

FAQ

Is fixed window better than no rate limiting?

Yes. Even imperfect rate limiting is better than none. Fixed window provides basic protection with minimal complexity.

How do I choose window size?

Common choices: 1 second (real-time), 1 minute (general APIs), 1 hour (daily quotas). Match the window to your use case.

Can I use fixed window for daily quotas?

Yes. A 24-hour window works well for daily API quotas. The boundary problem is less significant over long windows.

Does fixed window work with WebSocket connections?

For WebSocket messages, fixed window works but consider per-connection limits rather than per-IP.

How do I combine fixed window with user authentication?

Use the user ID as the key instead of IP. This provides consistent limits regardless of the user's network.

Mini Project

Build a fixed window rate limiter with automatic cleanup, proper headers, and configurable limits per route.

const express = require("express");
const app = express();

const store = new Map();

function createFixedWindowLimiter(windowMs, max) {
  return (req, res, next) => {
    const key = req.user?.id || req.ip;
    const windowId = Math.floor(Date.now() / windowMs);
    const storeKey = `${key}:${windowId}`;

    const count = (store.get(storeKey) || 0) + 1;
    store.set(storeKey, count);

    res.setHeader("X-RateLimit-Limit", max);
    res.setHeader("X-RateLimit-Remaining", Math.max(0, max - count));
    res.setHeader("X-RateLimit-Window", windowMs);

    if (count > max) {
      return res.status(429).json({ error: "Rate limit exceeded" });
    }

    next();
  };
}

const generalLimiter = createFixedWindowLimiter(60000, 60);
const strictLimiter = createFixedWindowLimiter(60000, 5);

app.use("/api", generalLimiter);
app.use("/api/login", strictLimiter);

setInterval(() => {
  const cutoff = Math.floor(Date.now() / 60000) - 2;
  for (const key of store.keys()) {
    if (parseInt(key.split(":")[1]) < cutoff) {
      store.delete(key);
    }
  }
}, 300000);

app.listen(3000);

What's Next

Now that you understand fixed window rate limiting, explore the sliding window algorithm for precise rate limiting. Then learn about implementing rate limiting in Express applications.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro