How to Block Bot Traffic in Next.js With No Extra Infra
Three patterns for bot blocking in Next.js — middleware, instrumentation, or preload — ranked by what works on Vercel, self-host, and the App Router.
How to Block Bot Traffic in Next.js With No Extra Infra
Next.js gives you three places to block bots, depending on your deployment target and runtime preference: Edge middleware, server instrumentation, or a Node preload. Each has tradeoffs. Here's when to use which.
For broader context on the firewall layer, see the SecureNow Firewall page.
Option 1: Edge middleware (works on Vercel, simplest)
middleware.ts runs before every matched request, in the Edge Runtime. Use it for IP-based blocking:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
const BLOCKED_IPS = new Set([
'185.220.101.42',
'185.220.102.245',
]);
const BAD_UA = /sqlmap|nikto|acunetix|masscan|nmap|python-requests/i;
const GOOD_BOTS = /googlebot|bingbot|gptbot|claudebot|perplexitybot/i;
export function middleware(request: NextRequest) {
const ip = request.headers.get('x-forwarded-for')?.split(',')[0]?.trim()
|| request.headers.get('x-real-ip')
|| '';
const ua = request.headers.get('user-agent') || '';
if (GOOD_BOTS.test(ua)) return NextResponse.next();
if (BLOCKED_IPS.has(ip)) return new NextResponse('Forbidden', { status: 403 });
if (BAD_UA.test(ua)) return new NextResponse('Forbidden', { status: 403 });
return NextResponse.next();
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};
The matcher skips static assets so you're not invoking edge middleware for every CSS file (matters on Vercel pricing).
This works on Vercel, Cloudflare Workers (via Open Next), and self-hosted Node. Limit: the static blocklist is whatever you hard-code. To stay current with attack traffic you'd need a refresh mechanism — which Edge Runtime restricts.
Option 2: Larger blocklist via Edge KV / fetch
For a refreshing blocklist on Vercel:
import { NextResponse } from 'next/server';
import { kv } from '@vercel/kv';
export async function middleware(request: NextRequest) {
const ip = request.headers.get('x-forwarded-for')?.split(',')[0]?.trim();
const isBlocked = await kv.sismember('blocklist:ips', ip);
if (isBlocked) return new NextResponse('Forbidden', { status: 403 });
return NextResponse.next();
}
Run a cron (Vercel Cron or external) to update blocklist:ips daily from threat-intel feeds. Workable but you're now maintaining the feed pipeline yourself.
Option 3: Node preload (self-hosted, full IP firewall)
If you self-host Next.js (Railway, Render, AWS, your own server), the simplest option is the Node-runtime preload:
npm install securenow
# Update your start script:
node -r securenow/firewall-only node_modules/next/dist/bin/next start
Or via NODE_OPTIONS:
NODE_OPTIONS='-r securenow/firewall-only' npm start
This puts the firewall at the HTTP-server layer below Next.js. Requests from blocked IPs are rejected before Next.js sees them. The blocklist is 500k entries refreshed hourly, with auto-allowlisting for legitimate crawlers. No middleware to maintain.
This is the lowest-effort production setup. The catch: it requires Node runtime, so it doesn't work on Vercel (Edge functions only). For Vercel deployments, stick with Option 1 or 2.
Option 4: Instrumentation (App Router, broader hook)
Next.js 14+ supports instrumentation.ts at the project root — a hook that runs once at server startup. Use it to initialize observability and security tools that need to be set up before any request handlers:
// instrumentation.ts
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
await import('securenow/register');
}
}
This gives you full SecureNow — firewall plus tracing plus AI investigation — when running on Node.js runtime. The check on NEXT_RUNTIME ensures you only initialize Node-specific code on the Node runtime; Edge runtime falls through to whatever middleware you have.
What to actually deploy
For most Next.js apps:
- Self-hosted Next.js (Railway, Render, AWS, etc.): Option 3 (
-r securenow/firewall-only) or Option 4 (instrumentation.ts). Maximum protection, zero maintenance. - Vercel: Option 1 (Edge middleware) for IP blocking based on a hard-coded list, plus Option 4 for full SecureNow on the Node-runtime portions.
- Cloudflare Pages / Open Next: Option 1 with KV-backed blocklist.
A useful matcher config
The default matcher catches everything. For better performance, exclude paths Next.js handles internally:
export const config = {
matcher: [
'/((?!api/health|_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml).*)',
],
};
This skips health checks, static assets, and SEO files from middleware — saves edge function invocations on Vercel and reduces unnecessary compute on self-host.
Verifying it works
For Edge middleware:
curl -i https://yourapp.com/ -H "User-Agent: sqlmap/1.5.7"
# expects: HTTP/1.1 403 Forbidden
For the SecureNow firewall:
npx securenow firewall status
# Shows count of currently blocked IPs and last refresh time
Related
Frequently Asked Questions
Does Next.js middleware run on Vercel Edge?
Yes. middleware.ts runs at the edge in Edge Runtime — limited Node API surface, but full access to request headers and the ability to return Response objects. IP blocking works fine at this layer.
Does middleware run on every request?
Yes by default. You can scope it to specific paths via the matcher config to skip static assets and reduce edge function invocations on Vercel pricing.
Can I use the SecureNow firewall on Vercel?
The HTTP-layer firewall (preload `securenow/firewall-only`) requires Node.js runtime. On Vercel, use Edge Runtime middleware for blocking, or deploy your Next.js app on a Node-runtime host (Railway, Render, AWS) for full firewall support.
Does the App Router change anything?
Not for middleware — `middleware.ts` works identically in both Pages Router and App Router. Instrumentation is App Router specific and offers a different hook point.
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