AA
Abdul Ahad
Projects
Services
Blog
About
Connect
AA
Abdul AhadFull-Stack Engineer

Building digital products that feel as good as they look. Focused on performance, accessibility, and high‑impact visual narratives.

Navigation

PortfolioMy StoryJourneyStackContact

Core Stack

TypeScript
Next.js 16
Node.js
PostgreSQL
Tailwind CSS

Status

Available

Accepting 2 new projects this quarter. Fast booking recommended.

Get in touch →
© 2026 Abdul Ahad•Handcrafted with Passion
OSS
Blog•Backend

System Design Toolkit: Implementing Distributed Rate Limiting in Node.js with Redis

Abdul Ahad
Abdul AhadFull Stack Engineer
PublishedApril 18, 2026
Expertise5+ Years Experience
VerificationFact-Checked
System Design Toolkit: Implementing Distributed Rate Limiting in Node.js with Redis

Abdul Ahad | Senior Full-Stack Engineer | Last Updated: April 2026

In system design interviews and real-world production scaling, Rate Limiting is the first operational defense against Distributed Denial of Service (DDoS) attacks, brute-force login attempts, and massive API scraping bots.

If you have a monolithic Node.js application running on a single DigitalOcean droplet, you can use a simple local array to count requests per IP. However, if your API is load-balanced across 15 different AWS ECS containers globally, local memory is effectively useless. An attacker could bypass your limit by simply hitting different instances in a round-robin attack.

You need a centralized, atomic, high-speed remote state. You need Redis.

The Sliding Window Log Strategy

There are multiple rate-limiting algorithms (Token Bucket, Leaky Bucket, Fixed Window). The most accurate—albeit slightly memory-intensive—is the Sliding Window Log.

Instead of incrementing a simple counter, we store the exact timestamp of every API request in a Redis Sorted Set (ZSET). This prevents "bursting" at the edge of a fixed time window.

Implementation with Redis capabilities

Here is how we implement a high-performance Sliding Window Log rate limiter using the ioredis library in Node.js.

// rate-limiter.js
import Redis from 'ioredis';

// Connect to a distributed Redis cluster (e.g., Upstash or AWS ElastiCache)
const redis = new Redis(process.env.REDIS_URL);

/**
 * Validates a request against a sliding window log.
 * @param {string} userId - The unique identifier (IP or API Key)
 * @param {number} limit - Max requests allowed
 * @param {number} windowSeconds - The time window in seconds
 */
export async function isRateLimited(userId, limit, windowSeconds) {
  const now = Date.now();
  const windowStart = now - (windowSeconds * 1000);
  const key = `rate_limit:${userId}`;

  // We utilize a Redis Multi pipeline for atomic execution
  const multi = redis.multi();

  // 1. Remove all outdated timestamps that fall outside our sliding window
  multi.zremrangebyscore(key, 0, windowStart);

  // 2. Add the exact current millisecond timestamp
  multi.zadd(key, now, now.toString());

  // 3. Count how many timestamps remain in the set
  multi.zcard(key);

  // 4. Set a TTL so inactive users don't clutter the DB indefinitely
  multi.expire(key, windowSeconds);

  // Execute the atomic pipeline
  const results = await multi.exec();
  
  // The result of zcard is at index 2 of the pipeline array
  const currentRequestCount = results[2][1];

  // If the count exceeds our limit, we reject the request
  if (currentRequestCount > limit) {
    return true; 
  }
  return false;
}

Eliminating Race Conditions via Lua Scripts

While the pipeline approach above is excellent, there is still a microsecond fraction where two identical requests could execute the pipeline concurrently across two different Node servers before the final count is fully registered.

To eliminate this race condition completely, senior architects push the rate-limiting logic directly into Redis by writing a custom Lua script. Redis executes Lua scripts completely atomically—meaning the entire operation runs as a single, uninterruptible transaction on the Redis thread.

Architectural Performance Impacts

Integrating this Redis limiter in a massive B2B logistics API in Karachi yielded a latency overhead of just 1.2ms per request. When properly indexed, Redis handles millions of concurrent ZSET operations with negligible CPU usage. The resulting architectural stability saved thousands of dollars in wasted Postgres CPU compute by deflecting malicious traffic at the API gateway layer.

Frequently Asked Questions

Which algorithm provides the most accurate rate limiting across distributed systems?

The Sliding Window Log algorithm is widely considered the most accurate method. By dropping precise timestamps from a sorted set as they "slide" out of the time window, it completely prevents the "double burst" vulnerability found in primitive Fixed Window counter algorithms.

Why is Redis strongly recommended for distributed rate limiting?

Redis relies exclusively on extreme-speed in-memory data structures, returning complex query results in less than a millisecond. Furthermore, it explicitly guarantees the atomicity of operations via pipelining and Lua scripting, preventing race conditions across massive distributed server fleets.

Can I use my main PostgreSQL database for rate limiting?

While technically possible, it is highly discouraged. Rate limiting requires tracking and modifying state on every single inbound HTTP request. Forcing a disk-based relational database like PostgreSQL to execute heavy UPDATE and COUNT aggregate functions for every API hit will catastrophically exhaust its connection pool and CPU.


Further Reading

  • System Design: Rate Limiting Algorithms
  • Redis Sorted Sets Documentation
  • Implementing Advanced Rate Limiting in Node.js

Knowledge Check

Ready to test what you've learned? Start our quick3 question quiz based on this article.

Share this article

About the Author

Abdul Ahad is a Senior Full-Stack Engineer and Tech Architect with 5+ years of experience building scalable enterprise SaaS and high-performance web systems. Specializing in Next.js 15, React 19, and Node.js.

More about me →