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.
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
| Scenario | Approach |
|---|---|
| Production NestJS, want maintenance-free | SecureNow preload |
| Need custom logic + reuse across multiple apps | NestJS middleware (above) |
| Need NestJS Guard semantics (per-route) | Custom Guard |
| Only blocking specific routes | Apply 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
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 9Five 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 9Fastify hooks (onRequest) and the SecureNow preload both work cleanly. Here's the production setup for IP blocking and user-agent filtering.
May 9