Safeguard
Security Guides•

How do you add rate limiting in NestJS with @nestjs/throttler?

Install @nestjs/throttler, bind ThrottlerGuard globally, tune per route limits and fix the trust proxy gap so every user is not counted as one IP.

Safeguard Security Team
3 min read

Install @nestjs/throttler, import ThrottlerModule with a ttl in milliseconds and a limit, then bind ThrottlerGuard globally with APP_GUARD. Add trust proxy if you sit behind a load balancer, so the tracker sees real client addresses. Use SkipThrottle and Throttle for exceptions.

Setup steps

The NestJS rate limiting docs describe this flow:

  1. Install the package.
  2. Configure ThrottlerModule with forRoot or forRootAsync. ttl is the time to live in milliseconds, and limit is the maximum number of requests within that window.
  3. Bind ThrottlerGuard. Any guard binding works. To apply it globally, register it as an APP_GUARD provider.
npm i --save @nestjs/throttler
@Module({
  imports: [
    ThrottlerModule.forRoot({
      throttlers: [{ ttl: 60000, limit: 10 }],
    }),
  ],
  providers: [{ provide: APP_GUARD, useClass: ThrottlerGuard }],
})
export class AppModule {}

Several windows at once

You can define named throttlers, for example 3 calls per second, 20 per 10 seconds and 100 per minute. Short windows catch bursts, long windows catch steady scraping. Reference the names in the Throttle and SkipThrottle decorators to change them per route or controller.

Per route decisions

Route typeSuggested approach
Login, password reset, signupTight limit, short and long windows
Public read endpointsModerate limit
Health checksSkipThrottle
Webhooks from known sendersSeparate handling, since senders may share addresses

SkipThrottle disables the throttler for a class or one route, and the docs show you can negate skipping for a single route inside a skipped class.

The proxy problem

The docs say that if your app runs behind a proxy, you should configure the HTTP adapter to trust the proxy. Enabling trust proxy lets you retrieve the original IP from X-Forwarded-For, and you can override getTracker in a guard subclass to use that header instead of req.ip. For Express the doc example sets trust proxy to loopback.

If you skip this, every request may look like it comes from the proxy, so one noisy client exhausts the limit for everyone, or the limit is far too lax. Be as specific as you can about which proxies to trust. Read Express trust proxy for why a loose value lets clients forge addresses.

Storage

The throttler keeps counts in storage, and the docs include a section on custom storages. With several app instances, make sure counts are shared, otherwise each instance enforces its own limit. For the Fastify platform equivalent, see Fastify rate limit stores.

What rate limits do not fix

They slow guessing, they do not stop a single expensive request. Combine them with body and header limits and timeouts. See API resource limits beyond rate limiting and how to set up API rate limiting.

Safeguard's API security scanning and policies can check whether endpoints enforce limits. Free scan: gold.safeguard.sh/scan.

Quick check

Is ttl in seconds?

In the NestJS docs example, ttl is in milliseconds, so 60000 means one minute.

Do I need getTracker if I enable trust proxy?

Often not, since req.ip then reflects the forwarded address. The docs show overriding getTracker as another way to read the header.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.