How to Block Bot Traffic in NestJS With No Extra Infra

NestJS guards, interceptors, and global middleware all give you bot-blocking hooks. Here's the cleanest pattern for each.

Lhoussine
May 9, 2026·6 min read

How to Block Bot Traffic in NestJS With No Extra Infra

NestJS gives you several places to add bot blocking. The cleanest is global middleware — it runs before guards, before interceptors, and before any handler logic. Here's the production setup.

For broader context see the SecureNow Firewall page.

The simplest setup: SecureNow preload

npm install securenow

# Update your start script:
node -r securenow/firewall-only dist/main.js

This sits below NestJS at the HTTP server level. Bad IPs get blocked before NestJS routes the request. 500k IPs, hourly refresh, automatic Googlebot/GPTBot allowlisting. No NestJS-specific code.

For full SecureNow (firewall + tracing + AI investigation):

node -r securenow/register dist/main.js

The hand-rolled NestJS middleware

If you want middleware-level control inside NestJS:

// src/firewall.middleware.ts
import { Injectable, NestMiddleware, HttpException, HttpStatus } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

const BLOCKED_IPS = new Set([
  '185.220.101.42',
]);

const BAD_UA = /sqlmap|nikto|acunetix|masscan|nmap|python-requests/i;
const GOOD_BOTS = /googlebot|bingbot|gptbot|claudebot|perplexitybot/i;

@Injectable()
export class FirewallMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const ip = (req.headers['x-forwarded-for'] as string)?.split(',')[0]?.trim()
      || req.socket.remoteAddress;
    const ua = req.headers['user-agent'] || '';

    if (GOOD_BOTS.test(ua)) return next();
    if (BLOCKED_IPS.has(ip || '')) throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
    if (BAD_UA.test(ua)) throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);

    next();
  }
}

Wire it globally in your AppModule:

// src/app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { FirewallMiddleware } from './firewall.middleware';

@Module({
  imports: [/* ... */],
  controllers: [/* ... */],
  providers: [/* ... */],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(FirewallMiddleware).forRoutes('*');
  }
}

When to use which

ScenarioApproach
Production NestJS, want maintenance-freeSecureNow preload
Need custom logic + reuse across multiple appsNestJS middleware (above)
Need NestJS Guard semantics (per-route)Custom Guard
Only blocking specific routesApply middleware via forRoutes('login')

With Fastify adapter

If you've switched NestJS to Fastify:

// main.ts
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter()
  );

  app.getHttpAdapter().getInstance().addHook('onRequest', async (req, reply) => {
    const ip = req.headers['x-forwarded-for']?.toString().split(',')[0]?.trim() || req.ip;
    if (BLOCKED_IPS.has(ip)) {
      reply.status(403).send('Forbidden');
    }
  });

  await app.listen(3000);
}
bootstrap();

Fastify hooks have different ergonomics but achieve the same effect.

Verifying it works

npx securenow firewall status

Or with curl against a known-bad UA:

curl -i https://yourapp.com/ -H "User-Agent: sqlmap/1.5.7"

Related

Frequently Asked Questions

Should I use a Guard, Interceptor, or Middleware?

Middleware for IP blocking (runs first, before guards). Guards for authorization-style checks. Interceptors for response transformation. Bot blocking belongs in middleware.

Does NestJS run on Node runtime?

Always. NestJS apps run on Node, so the SecureNow preload (`-r securenow/firewall-only`) works without modification.

Can I use Express middleware in NestJS?

Yes — NestJS sits on top of Express by default (or Fastify). Express middleware works directly via `app.use()` in main.ts.

What about Fastify-mode NestJS?

If you've configured NestJS with the Fastify adapter, use Fastify hooks instead. The preload still works since both use Node HTTP servers.

Recommended reading

Adding Backend Tracing to a Sentry Stack with OpenTelemetry

If your team uses Sentry for frontend errors and needs backend distributed tracing without doubling the Sentry bill, here's the OpenTelemetry path that doesn't make you choose.

May 9
How to Block Bot Traffic in Express With No Extra Infra

Five approaches to bot blocking in Express, ranked by effort vs. effectiveness. From a 5-line allowlist to a full IP-reputation firewall — all without Cloudflare, AWS WAF, or any new infrastructure.

May 9
How to Block Bot Traffic in Fastify With No Extra Infra

Fastify hooks (onRequest) and the SecureNow preload both work cleanly. Here's the production setup for IP blocking and user-agent filtering.

May 9