A Pragmatic Guide to Rate Limiting: NestJS + Redis + Nginx
🚦 “Rate limiting helps prevent service abuse, and protects performance and user experience.”
When building production APIs, rate limiting is one of those things that often gets added late — usually after the first abuse spike or load test failure.
I’ve found that adding basic rate limiting early, even if it’s not perfect, is a worthwhile trade-off. It doesn’t need to be complicated, but it should be layered. This post walks through how I implement it using NestJS, Redis, and optionally Nginx.
🧱 Why Layered Rate Limiting?
I like to think about rate limiting in two layers:
- Edge-level (Nginx) – Blocks requests before they hit your app
- App-level (NestJS + Redis) – Enforces per-user or per-IP rules with more logic
Using both gives you a fast first line of defense, and a more flexible backend layer for finer-grained control.
1. Nginx Rate Limiting (Quick, Simple, Blunt)
In most of my deployments, I use Nginx as a reverse proxy in front of the NestJS API. Here’s a basic config:
http {
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
proxy_pass http://nestjs_upstream;
# other proxy settings...
}
}
}
What this does:
- 10 requests/second per IP
- Allows small bursts of up to 20 requests
- Rejects excess traffic with 503
It isn’t perfect, but it is sufficiently fast and uses no extra resources inside your app.
2. App-Level Rate Limiting with NestJS + Redis
At the application level, for more flexibility:
- Per-user, not just IP
- Track API keys or auth headers
- Return JSON errors, not HTML
Step 1: Install Dependencies
pnpm add rate-limiter-flexible ioredis
Step 2: Set Up the Redis Client
// src/redis/redis.service.ts
import { Redis } from 'ioredis';
export const redis = new Redis({
host: 'redis',
port: 6379,
});
Step 3: Create a Rate Limiter Guard
// src/common/guards/rate-limit.guard.ts
import {
CanActivate,
ExecutionContext,
Injectable,
TooManyRequestsException,
} from '@nestjs/common';
import { RateLimiterRedis } from 'rate-limiter-flexible';
import { redis } from '../../redis/redis.service';
const rateLimiter = new RateLimiterRedis({
storeClient: redis,
keyPrefix: 'rl',
points: 10, // 10 requests
duration: 60, // per 60 seconds
});
@Injectable()
export class RateLimitGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const req = context.switchToHttp().getRequest();
const key = req.ip || 'anonymous';
try {
await rateLimiter.consume(key);
return true;
} catch {
throw new TooManyRequestsException(
'Too many requests. Please try again later.',
);
}
}
}
Step 4: Apply the Guard
// src/app.controller.ts
import { UseGuards, Controller, Get } from '@nestjs/common';
import { RateLimitGuard } from './common/guards/rate-limit.guard';
@Controller('api')
export class AppController {
@UseGuards(RateLimitGuard)
@Get('public-endpoint')
getStuff() {
return { ok: true };
}
}
You can also apply it globally using app.useGlobalGuards().
3. Optional: Customize by User or API Key
const userId = req.user?.id || req.headers['x-api-key'] || req.ip;
await rateLimiter.consume(userId);
This gives you more precise control — and you can adjust limits dynamically if needed.
🧪 Bonus: Test It in CI or Locally
If you’re using tools like supertest, add integration tests to ensure 429s are thrown when expected:
it('should rate limit after 10 requests', async () => {
for (let i = 0; i < 11; i++) {
await request(app.getHttpServer()).get('/api/public-endpoint');
}
const res = await request(app.getHttpServer()).get('/api/public-endpoint');
expect(res.status).toBe(429);
});
Final Thoughts
Rate limiting doesn’t have to be perfect, but it’s worth doing early — especially if your API is public or unauthenticated.
I’ve found that combining:
- Nginx for simple, IP-based throttling
- Redis + NestJS for granular limits
- Optional API key or session-based tracking
…gives you a setup that’s easy to maintain and hard to abuse.
If it prevents even one outage or abuse spike, it’s time well spent.
🏷 Tags
NestJS · Redis · Nginx · Backend Security · API Rate Limiting · Performance