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.
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 thenpx.
How blocking works outside your code
The rules live in SecureNow. Enforcement runs inside your app, in the SDK, before any of your code:
- The block list: single addresses and ranges that are blocked everywhere.
- Scoped blocks: an address or range blocked on one route, method, app or environment.
- 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
| Dimension | CLI flag | Values | Example |
|---|---|---|---|
| Who | <ip> argument | one address or a CIDR range | 198.51.100.77, 203.0.113.0/24 |
| Route | --route | a path | /api/auth/register |
| Route match | --mode | exact, prefix, regex | --mode exact |
| Method | --method | GET, POST, … (default: all) | --method POST |
| App | --app | an app key (omit = all your apps) | --app df7ab010-… |
| Environment | --env | production, staging, … (omit = every environment) | --env production |
| How long | --duration | minutes, hours or days (15m, 2h, 24h, 7d, 30d), or omit for "until removed" | --duration 24h |
| Why | --reason | free 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:
| Tool | What it does |
|---|---|
securenow_firewall_hits | who was blocked, rate limited or challenged, per rule and per address |
securenow_firewall_test_ip | would this address, path and method be blocked? |
securenow_blocklist_add | add a block (IP or CIDR, route, mode, method, app, environment, duration) |
securenow_blocklist_unblock | lift a block, keeping its history |
securenow_trusted_add | mark 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
| Symptom | Cause | Fix |
|---|---|---|
test-ip says NOT ENFORCED | the firewall is off for that app environment | npx securenow firewall enable --env <env> |
| Every request shows your load balancer's IP | the proxy isn't trusted, so its X-Forwarded-For is ignored | add the proxy's address to config.networking.trustedProxies |
| An attacker's IPs look random and change every request | the app is reachable directly and trusts forged X-Forwarded-For from a private network, or the attacker rotates proxies | keep 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 paths | an SDK older than 8.12.2 | npm 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.
Related
- Academy Episode 8: Block attackers in seconds, outside your code
- Rate limit per API key, per session or per IP in Node.js
- Let your coding agent block attackers and add rate limits over MCP
- Adding an IP firewall to Express without Cloudflare
- Block bot traffic in Express without extra infrastructure
- The SecureNow firewall · CLI reference · CLI + MCP
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
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 26Part 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 24Audit 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