Bot protection for Node.js and Express

An async Express middleware, no native dependencies, and a verdict you can act on before your route handler runs.

How ZeroBot fits Node.js

Node services tend to sit behind a load balancer or another proxy, which makes the real client IP easy to get wrong — reading req.ip without trusting the right proxy chain is the most common integration bug in this stack. ZeroBot takes the IP you give it, so the important part is extracting the true client address first. Once that is right, the middleware is a single fetch call, and you can keep it off your critical path by scoring only the routes that are actually worth protecting.

The integration

middleware/botGuard.js

module.exports = async function botGuard(req, res, next) { const r = await fetch('https://api.zerobot.info/v3/openapi', { method: 'POST', headers: { Authorization: `Bearer ${process.env.ZEROBOT_KEY}`, 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ ip: req.ip, domain: req.hostname, useragent: req.get('user-agent') || '', }), }).then((x) => x.json()).catch(() => null); if (r && r.risk_score >= 80) return res.status(403).end(); req.zerobot = r; next(); };

Full endpoint reference and an interactive tester are in the API docs. Get a key from the free tier — no card required.

What actually attacks Node.js sites

Scrapers hitting JSON endpoints

Node APIs return data cheaply and fast, which makes them an efficient scraping target. Volume grows quietly because each request costs your server very little.

Signup and trial abuse

Free-tier abuse from disposable email providers and rotating IPs. The cost is real: infrastructure, support and polluted analytics.

Denial by expensive route

Attacks that deliberately hit your most expensive endpoints — search, reports, exports — so the resource cost lands on you rather than on request volume.

Frequently asked questions

Do I need app.set("trust proxy")?

If you sit behind a load balancer, yes. Otherwise req.ip is the proxy address and every verdict will be wrong. Verify by logging req.ip in staging before you deploy.

Does it add latency to every request?

The call is a single HTTP round trip with a median under 50ms. On routes where that matters, score asynchronously and act on the verdict for the next request instead.

What happens when the API is unreachable?

The snippet above fails open by design — a null response lets the request through. ZeroBot also fails open server-side, so a ZeroBot outage does not become your outage.

Protect your Node.js app this week.

Free tier, real verdicts, and documentation with a built-in tester.

Get started free →

Bot protection for other stacks

WooCommerce Laravel Django Next.js Cloudflare Worker