Skip to content

Fastify Plugin

@webdecoy/fastify registers the Node SDK as a Fastify plugin. Detection runs on every request and the result is attached to 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 Fastify bot detection product page.

Terminal window
npm install @webdecoy/fastify @webdecoy/node
import Fastify from 'fastify';
import webdecoyPlugin from '@webdecoy/fastify';
const fastify = Fastify();
await fastify.register(webdecoyPlugin, {
apiKey: process.env.WEBDECOY_API_KEY,
threshold: 70,
skipPaths: ['/health']
});
fastify.post('/api/checkout', async (req, reply) => {
const { decision, confidence, bot_detected } = req.webdecoy;
if (decision === 'block') {
return reply.code(403).send({ error: 'Request blocked' });
}
if (decision === 'challenge') {
return reply.code(429).send({ error: 'Verification required' });
}
// Process checkout...
});

The SDK does not read WEBDECOY_API_KEY on its own. Pass it as apiKey, as above. Without an apiKey the plugin runs local checks only and nothing reports to your dashboard. Create the key under API Keys in the dashboard.

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. If nothing appears, check that the plugin is registered before your routes and that apiKey is passed with your key.

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
trustProxy Unset: uses request.ip How much of X-Forwarded-For to believe: a hop count, 'cloudflare', 'railway', or CIDRs of your proxies. See Client IP behind a proxy
getIP Derived from trustProxy Custom IP extraction. Overrides trustProxy
onBlocked 403 Forbidden Custom handler for blocked requests (enforce mode only)
onError Log and allow (fail open) Custom error handler
skipPaths None Paths to skip
rules [tripwire()] Local rules; with none configured, the built-in tripwire paths are armed. Pass rules: [] to opt out
honeytoken true when apiKey is set Inject a hidden trap link into HTML replies

Behind a load balancer or platform proxy, tell the plugin how many hops to trust, not Fastify:

await fastify.register(webdecoyPlugin, {
apiKey: process.env.WEBDECOY_API_KEY,
trustProxy: 1, // or 'cloudflare', 'railway', or ['10.0.0.0/8']
});

Since Fastify 5.12.1, a number passed to Fastify’s own trustProxy server option trusts no hop at all: a count cannot tell a proxy from a client connecting directly, so Fastify fails closed and request.ip stays the socket address. Every visitor then looks like your proxy. The plugin’s trustProxy counts from the right of X-Forwarded-For and is unaffected. Fastify’s CIDR and function forms of trustProxy still work, and the plugin uses request.ip when you leave its option unset.

With an apiKey, this is already on. The plugin injects a hidden honeytoken link into your HTML replies and arms the tripwire it points at:

import Fastify from 'fastify';
import webdecoy from '@webdecoy/fastify';
const app = Fastify();
await app.register(webdecoy, { apiKey: process.env.WEBDECOY_API_KEY });

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.

Injection happens in an onSend hook and only rewrites full text/html replies, so JSON, text and downloads are untouched and Fastify recomputes Content-Length for you.

webdecoyCaptchaPlugin mounts the self-hosted captcha endpoints for the Browser Client widget:

import { webdecoyCaptchaPlugin } from '@webdecoy/fastify';
await fastify.register(webdecoyCaptchaPlugin, {
secret: process.env.WEBDECOY_SECRET
});

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