Cloudflare Workers (npm)
Add Centinel bot detection to an existing Cloudflare Worker using the npm package.
Overview
This guide covers adding Centinel to an existing Cloudflare Worker project via the npm package. Requests flow through the Centinel validator before reaching your worker, blocking bots while letting legitimate traffic through.
Looking for the standalone version?
If you don't have an existing worker and want to paste a single file into the Cloudflare dashboard, see the Cloudflare Workers (standalone) guide instead.
Prerequisites
- Secret key from your dashboard
- An existing Cloudflare Worker project using Wrangler
Install
npm install @centinel/cloudflare-workerConfigure
Use a Wrangler secret. Do not add CENTINEL_SECRET_KEY to [vars]. A committed
wrangler.toml can expose the key.
npx wrangler secret put CENTINEL_SECRET_KEYImport activateCentinel and wrap your existing worker:
import { activateCentinel } from '@centinel/cloudflare-worker';
const myWorker = {
async fetch(request, env, ctx) {
// Your existing worker code
return new Response('Hello from my app');
},
};
export default activateCentinel(myWorker);Requests now go through the Centinel validator before reaching your worker. Bots are blocked; legitimate traffic passes through.
Options
Pass a second argument to activateCentinel:
export default activateCentinel(myWorker, {
// Only validate paths matching this pattern (tested against pathname)
protectedPathsInclusion: /\/api(\/|$)/,
// Skip paths matching this pattern (default: static assets like .css, .js, .png)
protectedPathsExclusion: /\.(css|js|png|jpg|woff2)$/i,
// Validator API timeout in milliseconds (default: 10000)
timeout: 5000,
// Structured JSON debug logging (default: false)
enableDebugging: true,
});Path matching
protectedPathsInclusion and protectedPathsExclusion are tested against the URL pathname (e.g. /api/users), not the full URL. Query strings and hostname are ignored.
Environment variables
Options can be overridden per-request via environment variables. Set in wrangler.toml, the dashboard, or .dev.vars:
| Variable | What it does | Default |
|---|---|---|
CENTINEL_SECRET_KEY | API key sent to the validator | (none, required) |
CENTINEL_VALIDATOR_URL | Validator API endpoint | https://validator.centinelanalytica.com/validate |
CENTINEL_TIMEOUT | Request timeout in ms | 10000 |
CENTINEL_ENABLE_DEBUGGING | Set to "true" for debug logs | false |
Env vars take precedence over options passed to activateCentinel(), so you can use different settings per environment without changing code.
How requests flow
- Request arrives at your Cloudflare Worker
activateCentinelchecks the path against inclusion/exclusion patterns- If protected, it POSTs request metadata (URL, method, IP, headers,
_centinelcookie) to the validator API - The validator returns a decision:
Request is forwarded to your worker. If the validator set cookies, they're added to the response.
Client → Centinel → Validator (allow) → Your Worker → ClientYour worker is not called. The client receives an HTML error page with the status code from the validator (default 403).
Client → Centinel → Validator (block) → 403 HTML pageYour worker is not called. The client receives a base64-decoded challenge page from the validator.
Client → Centinel → Validator (redirect) → Challenge pageThe URL didn't match any protected endpoint on the validator side. Request is forwarded to your worker normally.
Client → Centinel → Validator (not_matched) → Your Worker → ClientFail-open behavior
If the validator is down, slow, or returns an error, the worker forwards the request without validation. This keeps the site available.
A bad secret key, an authentication failure, or a validator response error can also forward a protected request to your origin without validation.
After repeated failures, the worker uses exponential backoff (1s, 2s, 4s, ... up to 5 minutes). This prevents repeated calls to a broken endpoint.
Standalone mode
If you don't have an existing worker, the default export works on its own:
import centinelWorker from '@centinel/cloudflare-worker';
export default centinelWorker;TypeScript
Type definitions are included:
import { activateCentinel, CentinelOptions, CentinelHandler } from '@centinel/cloudflare-worker';
const options: CentinelOptions = {
protectedPathsInclusion: /\/api\//,
timeout: 5000,
};
export default activateCentinel(myWorker, options);Verify
Visit your site and confirm normal traffic works. Check worker logs for "service": "CentinelAnalytics" entries. A response includes Server-Timing: validator;dur=<ms> when the worker gets a validator duration. A network error has no timing header.
Monitoring
- Real-time logs: Workers dashboard → Your worker → Logs → Begin log stream
- Debug mode: Set
CENTINEL_ENABLE_DEBUGGING = "true"in your env vars for detailed JSON logs at each step - Server-Timing header: A response includes
Server-Timing: validator;dur=<ms>when the worker gets a validator duration. A network error has no timing header
Changelog
- v1.2.1 — Edge case fixes