Adding an IP Firewall to Nuxt Without Cloudflare

On self-hosted Nuxt the SecureNow preload works. On Vercel-deployed Nuxt, server middleware + Vercel KV gives you the same outcome.

May 9, 2026·4 min read

Adding an IP Firewall to Nuxt Without Cloudflare

Nuxt 3 deploys in many ways — Node, Vercel, Cloudflare Workers, AWS Lambda. The right firewall approach depends on which runtime your code actually executes in.

Self-hosted Nuxt (Node runtime)

npm install securenow

# In your start command:
node -r securenow/firewall-only .output/server/index.mjs

500k IPs blocked, hourly refresh, automatic crawler allowlisting. The standard recommendation.

For full SecureNow:

node -r securenow/register .output/server/index.mjs

Adds tracing, log capture, and AI investigation.

Vercel-deployed Nuxt

Use server middleware with a Vercel KV-backed dynamic blocklist:

// server/middleware/firewall.ts
import { kv } from '@vercel/kv';

export default defineEventHandler(async (event) => {
  const ip = getRequestHeader(event, 'x-forwarded-for')?.split(',')[0]?.trim()
    || getRequestIP(event)
    || '';

  if (!ip) return;

  const blocked = await kv.sismember('blocklist:ips', ip);
  if (blocked) {
    setResponseStatus(event, 403);
    return 'Forbidden';
  }
});

Refresh the blocklist via a cron job:

// server/api/cron/refresh-blocklist.ts
export default defineEventHandler(async (event) => {
  const auth = getRequestHeader(event, 'authorization');
  if (auth !== `Bearer ${process.env.CRON_SECRET}`) {
    setResponseStatus(event, 401);
    return 'Unauthorized';
  }

  const ips = await fetchSecureNowIpdbBlocklist(); // your fetch logic
  await kv.del('blocklist:ips');
  await kv.sadd('blocklist:ips', ...ips);

  return `Refreshed ${ips.length} IPs`;
});

Schedule in vercel.json:

{
  "crons": [{ "path": "/api/cron/refresh-blocklist", "schedule": "0 * * * *" }]
}

Cloudflare Workers / other edge runtimes

Replace Vercel KV with the runtime-appropriate store:

  • Cloudflare Workers: Workers KV
  • Deno Deploy: Deno KV
  • Other edge platforms: Upstash Redis (HTTP-accessible)

The middleware logic is identical; only the store imports change.

Combining with rate limiting

Layer the rate-limit middleware (see the rate-limiting post) on top. The firewall blocks reputation-based; rate limiting handles per-IP request budgets.

Verifying

For self-host:

npx securenow firewall status

For Vercel KV-backed:

curl -i https://yourapp.com/ -H "X-Forwarded-For: 185.220.101.42"
# Expect: 403 Forbidden if IP is in blocklist

Related

Frequently Asked Questions

Does the SecureNow preload work on Vercel-deployed Nuxt?

No — Vercel runs your Nuxt code on serverless functions where Node preload flags don't apply. Use server middleware with a Vercel KV-backed dynamic blocklist instead.

What about Nitro's built-in deployment targets (Cloudflare Workers, etc.)?

Same logic as Vercel — you can't preload a Node module on edge runtimes. Use server middleware with a runtime-compatible store (Cloudflare KV, Workers KV, or HTTP-accessible Redis like Upstash).

Can I customize the blocklist?

Yes — add per-app rules from the SecureNow dashboard, CLI, or API. Custom rules sync to running SDK instances within ~10 seconds.

Recommended reading

AI Security Auditing, Part 1: Set Up Your AI Agent to Audit Your Code

Part 1 of the AI Security Auditing series. Set up an AI coding agent to audit your application's security locally — install SecureNow, connect your account, and run your first threat-model pass in minutes.

Jul 24
How to Audit Your App's Security With AI: The 2026 Guide

A practical, end-to-end guide to auditing your application's security with an AI coding agent — find vulnerabilities at the code level, build detections at the runtime level, and never upload your codebase to do it.

Jul 24
AI Security Auditing, Part 2: Audit Authentication & Sessions With AI

Part 2 of the AI Security Auditing series. Use an AI agent to audit your login, session, MFA, password-reset and magic-link flows for account-takeover paths — then catch the attempts in live traffic.

Jul 23