Block an IP, a Range or One Route Without a Deploy (Node.js)

Block one IP, a CIDR range, or a single route and method in a Node.js app within seconds, with no code change: setup, scoping, CLI, MCP, verification and undo.

Oct 27, 2026·11 min read

Block an IP, a Range or One Route Without a Deploy (Node.js)

When an attack is happening, the slowest path you have is a code change. A deny list in a middleware means a branch, a review, a build and a deploy before a single request is refused, and it only covers the routes someone remembered to wrap.

This guide shows how to block a single address, a whole range, or one route and method in a running Node.js app, from the terminal, the dashboard or your coding agent, without touching the code. Every command below can be copied as is; every output is from a real run against our lab app, which you can watch in Academy Episode 8: Block attackers in seconds, outside your code.

Quick start

Install the SDK once and start your app with the preload. If you followed Episode 2, this is already done.

npm install securenow
npx securenow login          # opens the browser once
npx securenow app connect    # writes .securenow/runtime.json for this app
node -r securenow/register app.js

Make sure the firewall is on for the environment you want to protect:

npx securenow firewall enable --env production

Then block. This is the exact block from Episode 8: a signup flood from one range, refused only on the register route, only in production, for 24 hours.

npx securenow blocklist add 203.0.113.0/24 \
  --route /api/auth/register --mode exact --method POST \
  --env production --duration 24h \
  --reason "Signup flood creating fake accounts"

About ten seconds later, every POST /api/auth/register from that range gets a 403, and the same addresses can still load every other page.

Commands use npx securenow. If you installed the CLI globally (npm install -g securenow), drop the npx.

How blocking works outside your code

The rules live in SecureNow. Enforcement runs inside your app, in the SDK, before any of your code:

  1. The block list: single addresses and ranges that are blocked everywhere.
  2. Scoped blocks: an address or range blocked on one route, method, app or environment.
  3. Challenges and rate limits: covered in Rate limit per API key, per session or per IP.

The rules are held in memory, so a check never waits on the network. Every 10 seconds the SDK asks whether anything changed and pulls the new version only when it has. You can see the sync in your server log:

[securenow] Firewall: synced 887 global blocked IPs (887 exact + 0 CIDR ranges) and 1 scoped block rules
[securenow] Firewall: synced 4 rate-limit rules

A blocked request never reaches your route:

HTTP/1.1 403 Forbidden
Content-Type: application/json

{"error":"Forbidden","ip":"203.0.113.77"}

If SecureNow can't be reached, your app keeps serving traffic (failMode: "open", the default).

How narrow a block can be

DimensionCLI flagValuesExample
Who<ip> argumentone address or a CIDR range198.51.100.77, 203.0.113.0/24
Route--routea path/api/auth/register
Route match--modeexact, prefix, regex--mode exact
Method--methodGET, POST, … (default: all)--method POST
App--appan app key (omit = all your apps)--app df7ab010-…
Environment--envproduction, staging, … (omit = every environment)--env production
How long--durationminutes, hours or days (15m, 2h, 24h, 7d, 30d), or omit for "until removed"--duration 24h
Why--reasonfree text, kept in the history--reason "Scraper"

Recipes

Block one address everywhere (every app and environment on your account), for a day:

npx securenow blocklist add 198.51.100.77 --duration 24h --reason "Hammering the API"

Add --app <key> to keep a block to one app; npx securenow apps list prints your app keys.

Block one address on one route only (here, a scraper pulling the full export):

npx securenow blocklist add 198.51.100.77 \
  --route /api/notes/export --mode exact --method GET \
  --env production --duration 24h --reason "Export scraper"

Block a range from everything under a prefix, in production only:

npx securenow blocklist add 203.0.113.0/24 --route /api/admin --mode prefix --env production --duration 7d

Block for fifteen minutes while you investigate:

npx securenow blocklist add 192.0.2.10 --duration 15m --reason "Investigating"

Paths are normalized before matching

Many servers answer more than one spelling of a path: Express routes case-insensitively and ignores a trailing slash. A block on /api/auth/register therefore also matches /API/AUTH/REGISTER, /api//auth/register and percent-encoded spellings, because rules are compared against the canonical form of the path (decoded, lower-cased, duplicate slashes collapsed). This is the real check from our run:

npx securenow firewall test-ip 203.0.113.9 --path /API/AUTH/REGISTER --method POST
BLOCKED — 203.0.113.9 is in the blocklist
Matched by: 203.0.113.0/24
Rule scope: POST exact:/api/auth/register
Request scope: POST /API/AUTH/REGISTER

Verify a block before and after

firewall test-ip answers "would this request be blocked?" for any address, path, method and environment, without sending traffic:

npx securenow firewall test-ip 203.0.113.50 --path /api/auth/register --method POST --env production
npx securenow firewall test-ip 203.0.113.50 --path /api/auth/csrf --method GET --env production

In our run the first was blocked (matched by the range) and the second allowed: the block is only where it should be. If the firewall is switched off for an environment, the answer says so:

NOT ENFORCED — firewall is disabled for this app (staging)

See what a block actually stopped

firewall hits shows every refusal, grouped by rule and by address:

npx securenow firewall hits --since 1h
npx securenow firewall hits --since 24h --action blocked --env production
npx securenow firewall hits --rule <rule-id>
Hits   blocked 618

By rule
   336  blocked    block 198.51.100.77 GET /api/notes/export      1 IP
   282  blocked    block 203.0.113.0/24 POST /api/auth/register   241 IPs

The dashboard shows the same thing on Blocked IPs → Block hits, and each entry in the block list shows the requests it stopped.

Undo a block

npx securenow blocklist list                     # full IDs in the first column
npx securenow blocklist unblock <id> --reason "Attack over"
Unblocked; block report and history retained

Blocks you add with --duration expire on their own. Being able to undo quickly is what makes it safe to block quickly.

From the dashboard

Blocked IPs → Add IP has the same options: the address or range, a reason, Block for (15 minutes to 30 days, or until removed), and under Advanced the app, environment, route, route match and method. Anyone on your team can add a block there without access to the repository.

From your coding agent

Connect SecureNow's MCP server once:

claude mcp add securenow -- npx securenow mcp

Cursor and Codex connect to the same server, and a hosted endpoint is available at https://api.securenow.ai/mcp. The agent then has these firewall tools:

ToolWhat it does
securenow_firewall_hitswho was blocked, rate limited or challenged, per rule and per address
securenow_firewall_test_ipwould this address, path and method be blocked?
securenow_blocklist_addadd a block (IP or CIDR, route, mode, method, app, environment, duration)
securenow_blocklist_unblocklift a block, keeping its history
securenow_trusted_addmark an address as trusted

The arguments the agent sent in our run, for the range block:

{
  "ip": "203.0.113.0/24",
  "method": "POST",
  "pathPattern": "/api/auth/register",
  "pathMatchMode": "exact",
  "environment": "production",
  "duration": "24h"
}

The full two-step workflow (investigate read-only, then apply what you approve) with copyable prompts is in Let your coding agent block attackers and add rate limits over MCP.

Trusted addresses and the allowlist are different things

  • Trusted IPs (securenow trusted add <ip> --label office) are left out of the block list your apps receive, even if an automation or a feed lists them. Use them for your office, your uptime monitor, your CI runners.
  • The allowlist is an only-allow mode: once it has entries, only the listed addresses can reach the app and everything else gets a 403. Use it to lock an internal tool to known networks, not to exempt a few addresses.
npx securenow trusted add 198.51.100.10 --label "Uptime monitor"
npx securenow allowlist add 192.0.2.0/24 --label "Office VPN" --env staging

More than your own list

Your blocks sit next to a list SecureNow maintains for you: addresses with a record of abuse, synced into every app by default and applied above a confidence threshold (75 by default).

The address list (not route-scoped blocks, which need the HTTP request) can also be enforced lower in the stack. Both options are off by default and set in .securenow/runtime.json:

"firewall": {
  "tcp": true,
  "iptables": false
}

tcp closes the connection before any HTTP is read. iptables adds the rules to the OS firewall on Linux and needs root or CAP_NET_ADMIN.

Troubleshooting

SymptomCauseFix
test-ip says NOT ENFORCEDthe firewall is off for that app environmentnpx securenow firewall enable --env <env>
Every request shows your load balancer's IPthe proxy isn't trusted, so its X-Forwarded-For is ignoredadd the proxy's address to config.networking.trustedProxies
An attacker's IPs look random and change every requestthe app is reachable directly and trusts forged X-Forwarded-For from a private network, or the attacker rotates proxieskeep the app port reachable only through your proxy, and limit by session or API key instead of by IP: see Why per-IP rate limits fail against rotating IPs
A route-scoped block misses capitalized pathsan SDK older than 8.12.2npm install securenow@latest

The SDK reads X-Forwarded-For only when the direct connection comes from a private, loopback or configured trusted proxy address, which is what stops clients spoofing their address on the public internet.

Block or rate limit?

Block when the traffic has no legitimate use: a range creating fake accounts, a scanner probing /.env, a scraper on an endpoint it should never call. Rate limit when the same endpoint also serves real users: logins, exports, search. The next guide covers that: Rate limit per API key, per session or per IP in Node.js.

Frequently Asked Questions

How long does a new block take to reach my app?

The SecureNow SDK checks for rule changes every 10 seconds and pulls the new version only when something changed. In our test run, the first 403s arrived 8 to 10 seconds after the block was created.

Can I block an IP on one route only?

Yes. Add --route with --mode exact, prefix or regex, and optionally --method. The address is refused only on that route and method; the rest of the app keeps working for it.

Does a block add latency to every request?

No network call is made per request. Rules are held in memory inside your app and checked before your route runs.

What happens if SecureNow is unreachable?

The default failMode is open: your app keeps serving traffic. Set config.firewall.failMode to closed only if you would rather refuse traffic during an outage.

How do I undo a block?

A block with --duration expires on its own. Any block can be lifted with securenow blocklist unblock <id>; its history and block report are kept.

Recommended reading

11 Signs Your AI-Built App Needs a Security Audit

Built your app with a coding agent? 11 warning signs it needs a security audit, each checkable in five minutes, and how to run the full audit yourself.

Oct 26
AI Security Audit Part 1: Set Up Your Agent to Audit Code

Part 1 of the AI security audit series: set up an AI coding agent to audit your app locally, connect SecureNow, and run your first threat-model pass.

Jul 24
AI Security Audit: How to Audit Your App's Code With AI

Audit your app's security with the AI coding agent you already use. Find code vulnerabilities, turn them into runtime detections, and never upload code.

Jul 24