Skip to content

Express Middleware

@webdecoy/express wraps the Node SDK as Express middleware: every request is analyzed, blocked requests get a 403 by default, and the detection result rides on req.webdecoy.

For package capabilities and positioning, see the Express bot detection product page.

Terminal window
npm install @webdecoy/express @webdecoy/node
import express from 'express';
import { webdecoy } from '@webdecoy/express';
const app = express();
app.use(webdecoy({
threshold: 70,
skipPaths: ['/health', '/static'],
onBlocked: (req, res, detection) => {
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...
});

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):

Terminal window
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 WEBDECOY_API_KEY is set.

Extends the core ProtectOptions (threshold, skipLocalAnalysis, metadata) with:

Option Default Description
getIP req.ip / x-forwarded-for Custom IP extraction
onBlocked 403 Forbidden Custom handler for blocked requests
onError Log and allow (fail open) Custom error handler
skipPaths None Paths to skip (health checks, static assets)
extractTLS false Capture JA3/JA4 TLS fingerprints from the socket
rules None Local rules such as tripwire()

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>');
});

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 }));

  • Node SDK: the core API, configuration, and detection response
  • Browser Client: the widget for the captcha endpoints