
Hardening Next.js Middleware: Implementing an early-abort edge filter to drop bot traffic with zero compute overhead
Hey everyone,
Wanted to share an architectural lesson learned from running an active launch phase on Vercel this week.
Our app is a modern full-stack Next.js/Supabase application. Over the last 24 hours, our logs caught a sustained automated botnet probe cycling through residential ISP blocks and cloud VPS infrastructure. The traffic footprint was predictable: spamming /wp-login.php, /wp-admin/index.php, and attempting to exfiltrate root .env files.
If you let this traffic route normally through your standard Next.js page routers, you end up wasting lambda execution cycles, muddying your analytics logs, and unnecessarily waking up your database connection pools.
- Moving Security to Proxy Entry To completely isolate our backend from this background noise, we implemented an early-return security filter right after pathname string extraction inside apps/web/proxy.ts, before any authentication logic or ledger routines evaluate:
const url = new URL(request.url);
const path = url.pathname.toLowerCase();
if (path.startsWith('/wp-') || path.includes('.env') || path.startsWith('/.env')) {
return new Response('Forbidden', { status: 403 });
}
Because this executes at the earliest proxy entry threshold, the automated scripts eat an immediate 403 Forbidden response from the edge global network. Lambda invocation stays at zero.
- Satori / Edge OG Image Font Crashes While monitoring this, we also hit a production runtime error in our Open Graph dynamic generation endpoint (/api/og/verify/[id]): Error: Unsupported OpenType signature wOF2.
If you are using Satori for dynamic canvas generation, keep this in mind: Satori does not support Brotli-compressed WOFF2 font files. If you point your font loaders to modern .woff2 assets, the layout processor reads the compressed signature header and throws a hard exception.
We resolved this by swapping our asset destinations to standard uncompressed WOFF1 (.woff) binaries and hardening the asset loader with four defensive validation rules:
Memory Capping: Implemented a strict 32-entry cache limit (FONT_CACHE_MAX) with a 1-hour TTL window to prevent container heap exhaustion.
Timeouts: Wrapped our fetch requests in an AbortController capped at 5 seconds with exponential backoff retries to prevent hung CDN calls from freezing the lambda.
Mime Validation: Explicitly validating the content-type header to prevent edge cases where a CDN 404 HTML page gets parsed as a font stream.
Graceful Degradation: Moved from Promise.all to Promise.allSettled. If a specific style variant experiences network latency, the asset engine drops to system fallbacks and finishes rendering the canvas instead of breaking the page.
Our stack is now running in a stable, permanent green posture. If anyone is running into similar Satori layout limits or edge proxy bottlenecks on Vercel, ask away below—happy to drop our exact middleware configurations.
You can view our live rendering engine and verification setups here: vauntico.com