# API Rate Limiting in NestJS with Redis: A Production Guide

> How to implement distributed rate limiting in NestJS using @nestjs/throttler and Redis — protecting your API from abuse at any scale.

URL: https://www.matthewswong.com/en/blog/api-rate-limiting-nestjs-redis/

- Author: Matthews Wong (https://www.matthewswong.com/en/about/)
- Published: 2025-06-10
- Updated: 2025-06-10
- Category: Web Dev

Rate limiting is one of those features that feels optional until the day someone discovers your endpoint and sends 10,000 requests in 60 seconds, taking down your database. NestJS's @nestjs/throttler module makes implementing rate limiting straightforward, but the default in-memory storage breaks when you scale to multiple instances. Redis-backed rate limiting is the production-correct approach. I've implemented this on NestJS APIs serving ERP and SaaS applications where multiple server instances run behind a load balancer.

## The Problem with In-Memory Rate Limiting

The default @nestjs/throttler uses in-memory storage — a Map that tracks request counts per client IP per TTL window. This works perfectly for a single-instance deployment. The moment you add a second instance behind a load balancer, instance A has no knowledge of requests hitting instance B. A client can send 100 requests to instance A and 100 requests to instance B before either triggers the limit. Effective rate limit: doubled. Redis solves this by providing shared state across all instances.

### Installing the Redis Throttler

Install the required packages: @nestjs/throttler and @nest-lab/throttler-storage-redis. The throttler-storage-redis package implements the ThrottlerStorage interface using Redis as the backend. Configure ThrottlerModule in your AppModule with the Redis storage provider. The ThrottlerStorageRedisService accepts your Redis connection config (host, port, password). Register ThrottlerGuard globally using the APP_GUARD token in your providers array so every route is protected by default.

> From implementing rate limiting on an ERP NestJS API: use multiple throttle configurations for different endpoint types. A public authentication endpoint (login, password reset) gets aggressive limits: 5 requests per minute. A general API endpoint gets relaxed limits: 100 requests per minute. A bulk export endpoint gets very strict limits: 2 requests per 5 minutes. Use the @Throttle() decorator on specific controllers or routes to override the global config. This gives you surgical control without duplicating the global guard setup.

## Implementing Throttler with Redis

The ThrottlerModule configuration in AppModule: import ThrottlerModule with an array of throttle configurations (each has a name, ttl in milliseconds, and limit). The storage option points to your ThrottlerStorageRedisService. Register APP_GUARD with ThrottlerGuard in the providers array. Every route in your application now enforces the rate limit. Endpoints that should bypass rate limiting (health checks, internal monitoring) use the @SkipThrottle() decorator.

### Custom Rate Limiting by User vs IP

The default throttler keys requests by IP address. For authenticated APIs, you want to key by user ID — one IP can represent a company's entire team. Override ThrottlerGuard's getTracker() method to return the user ID from the JWT payload if authenticated, falling back to IP for unauthenticated requests. This prevents the legitimate scenario where 20 people at the same company (same IP via NAT) trigger rate limits designed for individual users.

> **Rate Limit Headers and User Experience**

> When a request is rejected with 429 Too Many Requests, always include the Retry-After header — it tells the client when they can retry. Without it, clients typically implement exponential backoff from scratch or retry immediately (worsening the problem). NestJS throttler adds X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers to successful responses automatically. For rejected requests, add a Retry-After header in your ThrottlerExceptionFilter to tell clients exactly when to retry. Good rate limiting is transparent to legitimate users and informative when limits are hit.

## Redis Key Strategy and TTL

@nest-lab/throttler-storage-redis stores throttle state in Redis keys with the format throttle:'<'tracker>:'<'ttl>. Each key has an automatic TTL matching your throttle window. When the TTL expires, the key is deleted and the count resets. Redis's atomic INCR operation ensures concurrent requests from the same client don't race past the limit. The keys are lightweight — a throttle record is a single counter value. Even a high-traffic API with 10,000 concurrent users generates only 10,000 Redis keys with sub-millisecond read/write operations.

## What I Use and Why

For any NestJS API serving more than one instance, Redis-backed throttling with @nestjs/throttler + @nest-lab/throttler-storage-redis is my standard. The setup takes less than an hour and provides meaningful protection against abuse. I configure three throttle tiers: strict for auth endpoints, standard for regular API, and lax for read-only public endpoints. I override the tracker to use user ID for authenticated routes. I add a custom exception filter to enrich 429 responses with human-readable retry information. This combination has prevented abuse incidents on every API I've deployed it to.

## Beyond Rate Limiting: Defense in Depth

Rate limiting is one layer. Production API protection needs multiple: (1) Rate limiting (NestJS Throttler + Redis) — prevents request floods. (2) Input validation (class-validator + class-transformer pipes) — prevents malformed inputs. (3) Authentication guards (Passport JWT) — prevents unauthenticated access. (4) Authorization guards (CASL or custom) — prevents accessing resources you don't own. (5) SQL injection prevention (Prisma's parameterized queries). (6) Network-level DDoS protection (Cloudflare or equivalent). Rate limiting handles the traffic layer; the other layers handle the application layer.

## Sources & Further Reading

- NestJS Docs — Rate Limiting — https://docs.nestjs.com/security/rate-limiting
- Wanago — API with NestJS #187: Rate Limiting Using Throttler — https://wanago.io/2025/03/03/api-nestjs-rate-limiting-throttler/
- DEV Community — Redis as Custom Storage for NestJS Rate Limiter — https://dev.to/zsevic/redis-as-custom-storage-for-nestjs-rate-limiter-4o9k

## Source links

- https://docs.nestjs.com/security/rate-limiting
- https://wanago.io/2025/03/03/api-nestjs-rate-limiting-throttler/
- https://dev.to/zsevic/redis-as-custom-storage-for-nestjs-rate-limiter-4o9k

## FAQ

### Why doesn't in-memory rate limiting work in a multi-instance NestJS deployment?

The default @nestjs/throttler uses an in-memory Map to track request counts per client IP. When multiple instances run behind a load balancer, each instance maintains its own isolated counter — so a client can exhaust one instance's limit on instance A and still send the same number of requests to instance B. The effective rate limit becomes multiplied by the number of instances.

### How does Redis fix the distributed rate limiting problem?

Redis provides shared state across all NestJS instances. The @nest-lab/throttler-storage-redis package implements the ThrottlerStorage interface using Redis as the backend, so every instance reads and writes to the same counter. Redis's atomic INCR operation also ensures concurrent requests from the same client cannot race past the limit.

### Why should rate limits key by user ID for authenticated APIs rather than by IP address?

A single IP can represent an entire company's team when employees share a network via NAT. If rate limits are enforced per IP, 20 people at the same company could collectively trigger a limit designed for one individual user. Overriding ThrottlerGuard's getTracker() method to return the JWT user ID for authenticated requests prevents this false positive while falling back to IP for unauthenticated traffic.

### What throttle tiers does the post recommend, and how are they applied?

The post recommends three tiers based on endpoint sensitivity: strict (5 requests per minute) for public auth endpoints like login and password reset, standard (100 requests per minute) for general API endpoints, and very strict (2 requests per 5 minutes) for bulk export endpoints. The global ThrottlerGuard enforces the defaults, and the @Throttle() decorator on specific controllers or routes overrides them for surgical control.

### What should a 429 response include to give clients a good experience?

A 429 Too Many Requests response should always include a Retry-After header that tells the client exactly when it can retry. Without it, clients typically implement exponential backoff from scratch or retry immediately, which worsens the problem. NestJS throttler automatically adds X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers to successful responses; a custom ThrottlerExceptionFilter can add Retry-After to rejected responses.

---

- HTML version: https://www.matthewswong.com/en/blog/api-rate-limiting-nestjs-redis/
- Bahasa Indonesia: https://www.matthewswong.com/id/blog/api-rate-limiting-nestjs-redis/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
