Article

How to Implement Rate Limiting in Production (Node.js + Redis) — A Simple Guide

·5 min read min read·👁 32
Dharmendra Singh Yadav

Dharmendra Singh Yadav

Founder, Dharmsy Innovations

How to Implement Rate Limiting in Production (Node.js + Redis) — A Simple Guide

f you're running an API or backend service, one of the most important things you need is rate limiting. It protects your server from spam, DDoS-like bursts, and accidental abuse. Without it, even a simple endpoint can get overloaded and slow down your entire system.

In this guide, we’ll walk through:

  1. What rate limiting actually is
  2. Why Redis is perfect for it
  3. Simple real-world code examples using Node.js
  4. Best practices for production

Let’s make it super easy to understand.

What is rate limiting? (In simple words)

Rate limiting means:

👉 “You can’t send unlimited requests. You’re allowed only X requests in a given time frame.”

Example:

  1. 100 requests per minute
  2. 5 login attempts every 10 minutes
  3. 20 API calls per second

This keeps your system safe from:

  1. Bots
  2. Brute-force attacks
  3. Accidentally buggy code
  4. Sudden traffic spikes

Why use Redis for rate limiting?

Redis is:

  1. Super fast (in-memory)
  2. Can handle millions of operations per second
  3. Supports atomic operations (no race conditions)
  4. Works across multiple Node.js servers

This makes it perfect for production-level rate limiting.

Rate Limiting Methods (Explained Like You're 18)

1. Fixed Window

You count how many requests happen in a fixed block of time.

Example: 100 requests in 1 minute.

Pros: Easy and fast

Cons: Users can break limits at the edges of windows

2. Sliding Window

You look at the last exact X seconds instead of a fixed block.

Pros: More accurate, smoother

Cons: Slightly heavier in Redis

3. Token Bucket

Imagine a bucket that slowly fills with tokens.

Each request takes 1 token. If bucket empty → request blocked.

Pros: Best for production, allows small bursts

Example 1 — Easy Rate Limiting (Fixed Window)

Perfect for simple APIs, IP-based limits, or small projects.

// fixedWindowRateLimit.js
const Redis = require("ioredis");
const redis = new Redis();

module.exports = function rateLimit({
windowSeconds = 60,
maxRequests = 100
}) {
return async (req, res, next) => {
const key = `rl:${req.ip}`;
const count = await redis.incr(key);

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

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

next();
};
};

Usage:

app.use(rateLimit({ windowSeconds: 60, maxRequests: 100 }));

Example 2 — Sliding Window (More Accurate)

This stores timestamps of each request in a Redis sorted set.

// slidingWindow.js
const Redis = require("ioredis");
const redis = new Redis();

module.exports = ({ windowSeconds = 60, maxRequests = 100 }) => {
return async (req, res, next) => {
const key = `sw:${req.ip}`;
const now = Date.now();
const windowStart = now - windowSeconds * 1000;

await redis.zremrangebyscore(key, 0, windowStart);

const count = await redis.zcard(key);

if (count >= maxRequests) {
return res.status(429).json({ message: "Rate limit reached" });
}

await redis.zadd(key, now, now.toString());
await redis.expire(key, windowSeconds + 2);

next();
};
};

Example 3 — Token Bucket (Best for Production)

This works smoothly even when users send burst traffic.

We use a Redis Lua script so everything is executed safely and atomically.

👉 But don’t worry, you can just copy-paste this code and it works.

// tokenBucket.js
const Redis = require("ioredis");
const redis = new Redis();

const script = `
local key = KEYS[1]
local now = tonumber(ARGV[1])
local refill_rate = tonumber(ARGV[2])
local capacity = tonumber(ARGV[3])
local tokens_needed = 1

local data = redis.call("HMGET", key, "tokens", "last")
local tokens = tonumber(data[1]) or capacity
local last = tonumber(data[2]) or now

local delta = now - last
local refill = delta * refill_rate
tokens = math.min(capacity, tokens + refill)
last = now

if tokens < tokens_needed then
return {0, tokens}
end

tokens = tokens - tokens_needed
redis.call("HMSET", key, "tokens", tokens, "last", last)
redis.call("EXPIRE", key, 60)

return {1, tokens}
`;

module.exports = function tokenBucket({
capacity = 20,
refillPerSec = 1
}) {
return async (req, res, next) => {
const now = Date.now();
const key = `tb:${req.ip}`;
const refillPerMs = refillPerSec / 1000;

const result = await redis.eval(script, 1, key, now, refillPerMs, capacity);

const allowed = result[0];

if (allowed == 1) return next();

return res.status(429).json({
message: "Too many requests, slow down."
});
};
};

Where Should You Use Rate Limiting?

✔ Login routes

✔ OTP routes

✔ Public APIs

✔ Expensive database queries

✔ Upload endpoints

✔ Comment endpoints

✔ Chat message endpoints

Basically anywhere users can spam.

Best Practices for Production

✅ Use userId instead of IP when possible

Because one office can share IPs.

✅ Always set TTL so old keys auto-clean

Redis memory stays low.

✅ Fail safely

If Redis goes down:

  1. You can block all requests (fail closed)
  2. Or allow all requests (fail open) → recommended

✅ Use multiple layers

  1. Cloudflare / AWS ALB rate limiting
  2. Redis rate limiting
  3. App-level validation

✅ Return helpful headers

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 50
X-RateLimit-Reset: 170562001

✅ Monitor

  1. How many 429 errors users get
  2. Redis memory
  3. Unusual spikes

✅ Use different limits per route

Example:

  1. Login route: 5 attempts/min
  2. Public API: 100 req/min
  3. Chat: 20 msgs/min


Rate limiting is not optional anymore — it’s a required part of any real production backend.

Redis makes it:

  1. Fast
  2. Accurate
  3. Safe
  4. Scalable

Start with the simple fixed-window method.

If your app grows, upgrade to sliding window or token bucket.

Frequently Asked Questions

What is rate limiting? (In simple words)?+

Rate limiting means:

What is Fixed Window?+

You count how many requests happen in a fixed block of time.

What is Sliding Window?+

You look at the last exact X seconds instead of a fixed block.

What is Token Bucket?+

Imagine a bucket that slowly fills with tokens.

How can Dharmsy help?+

Dharmsy builds production-grade web, mobile, and SaaS products. Share your requirements and we'll give you a clear, honest plan.

Work with Dharmsy Innovations

Turn Your SaaS or App Idea Into a Real Product — Faster & Affordable

Dharmsy Innovations helps founders and businesses turn ideas into production-ready products — from MVP and prototypes to scalable platforms in web, mobile, and AI.

No sales pressure — just honest guidance on cost, timeline & tech stack.