Express Middleware
@webdecoy/express wraps the Node SDK as Express middleware: every request is analyzed and the detection result rides on req.webdecoy. It runs in monitor mode by default: verdicts are recorded and reported, and every request is served. Set mode: 'enforce' to answer blocking verdicts with a 403 (or your onBlocked handler).
For package capabilities and positioning, see the Express bot detection product page.
Installation
Section titled “Installation”npm install @webdecoy/express @webdecoy/nodeQuick Start
Section titled “Quick Start”import express from 'express';import { webdecoy } from '@webdecoy/express';
const app = express();
app.use(webdecoy({ apiKey: process.env.WEBDECOY_API_KEY, threshold: 70, skipPaths: ['/health', '/static'], // mode: 'enforce', // default is 'monitor': nothing is blocked onBlocked: (req, res, detection, next, decision) => { // runs only in enforce mode res.status(403).json({ error: 'Blocked' }); }}));
app.post('/api/login', (req, res) => { const { decision, confidence, threat_level } = req.webdecoy;
if (decision === 'challenge') { return res.status(429).json({ error: 'Please complete verification' }); }
// Process login...});The SDK does not read WEBDECOY_API_KEY on its own. Pass it as apiKey, as above. Without an apiKey the middleware runs local checks only and nothing reports to your dashboard. Create the key under API Keys in the dashboard.
Verify Your Install
Section titled “Verify Your Install”Send one request with the reserved test User-Agent. It always fires a detection through the real pipeline, works on localhost before you deploy, and shows up labeled Test in the dashboard (excluded from stats and billing):
curl -A "WebDecoy-Test/1.0" http://localhost:3000/Within a few seconds a detection with the Test category chip appears on the Detections page. In the default monitor mode the request is served normally; with mode: 'enforce' the curl gets a 403, so you can see the middleware act from the terminal alone. If nothing appears in the dashboard, check that the middleware is mounted before your routes and that apiKey is passed with your key.
Options
Section titled “Options”Accepts the core SDK configuration and ProtectOptions (threshold, skipLocalAnalysis, metadata), plus:
| Option | Default | Description |
|---|---|---|
mode |
'monitor' |
'monitor' records verdicts and serves every request; 'enforce' blocks |
honeytoken |
On when apiKey is set |
Inject a hidden honeytoken link into HTML responses and arm its tripwire |
trustProxy |
Express req.ip |
How much of X-Forwarded-For to believe: a hop count, 'cloudflare', 'railway', or proxy CIDRs |
getIP |
None | Custom IP extraction (overrides trustProxy) |
onBlocked |
403 Forbidden | Custom handler for blocked requests (enforce mode only); receives next and the full decision |
onError |
Log and allow (fail open) | Custom error handler |
skipPaths |
None | Paths to skip (health checks, static assets) |
rules |
[tripwire()] |
Local rules; with none configured, the built-in tripwire paths are armed. Pass rules: [] to opt out |
Set a Scraper Trap
Section titled “Set a Scraper Trap”Tripwires run locally, with no API key and no network call. And under session clearance, a tripwire hit from a tokened session feeds the clearance deny-list: the SDK forwards the session’s token with the violation, and the client gets the same durable, rotation-proof lockout as a hosted decoy hit.
With an apiKey, this is already on. The middleware injects a hidden
honeytoken link into your HTML responses and arms the tripwire it points at, with no
wiring and no template edit:
import { webdecoy } from '@webdecoy/express';
app.use(webdecoy({ apiKey: process.env.WEBDECOY_API_KEY, skipPaths: ['/health', '/static']}));The path is derived from your API key by HMAC, so every replica advertises and
arms the same one. Set honeytoken: false to opt out.
Only full text/html responses are rewritten. JSON, text and downloads are left
alone, and a response that has already committed a Content-Length is skipped so
the body can never truncate at the client.
To place the link yourself, or to add extra bait paths:
import { tripwire, honeytoken } from '@webdecoy/node';
const trap = honeytoken();
app.use(webdecoy({ honeytoken: false, rules: [tripwire({ paths: [trap.path] })],}));
app.get('/', (req, res) => { res.send('<!doctype html><html><body>' + trap.linkHtml + '</body></html>');});Captcha Endpoints
Section titled “Captcha Endpoints”webdecoyCaptcha mounts the self-hosted captcha’s challenge and verify endpoints, which the Browser Client widget talks to:
import { webdecoyCaptcha } from '@webdecoy/express';
app.use(express.json());app.use(webdecoyCaptcha({ secret: process.env.WEBDECOY_SECRET }));Next Steps
Section titled “Next Steps”- Node SDK: the core API, configuration, and detection response
- Browser Client: the widget for the captcha endpoints