Back to Blog
Lesson 19 of the Redis: Redis Essentials & Data Types course
August 6, 20264 min read

The Fixed Window Rate Limiting Pattern: A Practical Guide

Master the fixed window rate limiting pattern to control API traffic. Learn how to track requests and enforce limits using atomic Redis operations.

A vibrant green shuttered window on a white wall with a directional blue arrow sign.

Previously in this course, we explored mastering INCR and DECR to handle atomic operations. Now, we will apply those concepts to build a foundational defense mechanism: the fixed window rate limiting pattern.

Rate limiting is essential for maintaining API health and security. By capping the number of requests a client can make within a specific timeframe, you prevent resource exhaustion and mitigate brute-force attacks, as discussed in our overview of rate limiting and OWASP security.

Understanding the Fixed Window Algorithm

The fixed window algorithm works by dividing time into discrete, non-overlapping blocks. If you set a limit of 100 requests per minute, the "window" starts at the beginning of the minute (e.g., 12:00:00) and ends at the close of that minute (12:00:59).

Within this window, every request increments a counter. Once the counter hits the threshold, you reject subsequent requests until the clock resets to the next window.

Why Fixed Window?

  • Simplicity: It is the easiest algorithm to implement and reason about.
  • Memory Efficient: You only need to store one counter and one expiration per client/window.
  • Performance: Incrementing a key is an $O(1)$ operation in Redis.

While more complex approaches like sliding window rate limiting offer more precision at the edges of time boundaries, the fixed window is often sufficient for basic traffic shaping.

Implementing the Pattern

To implement this, we combine the INCR command with EXPIRE. When a request arrives, we check if a key for the current window exists. If not, we create it and set a TTL (Time-To-Live) equal to the window size.

Worked Example: Node.js Logic

In our ongoing project, we will add a rateLimit middleware.

JAVASCRIPT
async function checkRateLimit(userId) {
  const windowSize = 60; // 60 seconds
  const limit = 100;     // 100 requests
  const key = CE9178">`rate_limit:${userId}:${Math.floor(Date.now() / 1000 / windowSize)}`;

  // Use a transaction or simple atomic flow
  const count = await redis.incr(key);

  if (count === 1) {
    // First request in this window, set expiration
    await redis.expire(key, windowSize);
  }

  if (count > limit) {
    throw new Error(CE9178">'Rate limit exceeded');
  }
  
  return count;
}

Hands-on Exercise

  1. Define your window: Choose a user ID and set a limit of 5 requests per 30 seconds.
  2. Code the increment: Write a script that calls INCR on a key formatted as limiter:{userId}:{timestamp_window}.
  3. Verify the TTL: Use the TTL command in your CLI to ensure the key automatically expires at the end of your 30-second window.
  4. Test the overflow: Run a loop in your code to fire 6 requests rapidly and verify that the 6th request triggers your error handling.

Common Pitfalls

  • Boundary Spikes: Because the window resets exactly at the start of the next minute, a user could theoretically send 100 requests at 12:00:59 and another 100 at 12:01:00. This "double-burst" effectively allows 200 requests in a two-second span.
  • Clock Skew: Relying on the server's local time is fine for a single instance, but in distributed systems, ensure your application servers are synchronized via NTP.
  • Missing Expiration: Never use INCR without EXPIRE. Without an expiry, your Redis instance will eventually fill up with stale counter keys, leading to memory pressure.

FAQ

Does the fixed window handle distributed environments? Yes, because Redis acts as a centralized data store. All your backend instances point to the same Redis key, ensuring the count is globally consistent for that user.

Is this the most secure method? It is effective for general traffic control. However, for high-stakes endpoints, consider the system design tradeoffs of rate limiting algorithms to see if a more sophisticated approach is required.

What happens if the server crashes after INCR but before EXPIRE? The key will persist indefinitely, leaking memory. In production, we use Lua scripts to execute the increment and expiration as a single atomic operation, which you will master in an upcoming lesson.

Recap

We have defined the fixed window as a time-bound bucket for request counting. By using INCR and EXPIRE together, we create a robust, simple gatekeeper for our API. Remember: always pair your counters with a TTL to keep your memory usage clean.

Up next: We will build a complete, functional rate limiter that you can drop into your API project.

Similar Posts