Skip to content

Clearance Enforcement

Most bot defenses enforce on IP or fingerprint, both of which rotate. Clearance enforcement flips the model: real browsers earn a signed wd_clearance token, and enforcement checks one thing per request: does this request carry a valid token? A bot on 10,000 IPs still needs a valid token on each request and can’t mint one. A real human on any IP passes silently.

The rotation-proof part is the loop with your deception surfaces: if a session that holds a token trips one of your decoys (or an SDK tripwire planted in your own pages), WebDecoy denies that browser’s fingerprint: its token is revoked within about a minute and it can never mint another, on any IP.

At the edge, each request passes if any of these hold, checked in order:

  1. Machine service token: a credential you issued for a partner, monitor, or job (see Response Actions).
  2. Verified bot: Googlebot, Bingbot, AI crawlers, and monitoring services you’ve allowed (verified by the platform / reverse-DNS, never by User-Agent).
  3. Unscoped route: the path isn’t in your token-enforced route list, so no token is required.
  4. Valid wd_clearance cookie: a real browser that already passed the client check, and whose fingerprint hasn’t been denied by tripping a decoy.

If none hold:

  • Monitor mode: the request passes anyway, tagged with an x-wd-clearance header so you can see what enforce mode would do.
  • Enforce mode: the browser gets a one-time invisible check that mints a token and reloads; non-browser clients get a 403.

Configure everything on the Enforcement page (sidebar → Enforcement). It is a set of routed sub-pages, which separates the things you set once from the things you come back to:

Sub-page What it owns
Overview (/enforcement) the Monitor/Enforce switch and the readiness checks that must pass before Enforce is available, the decision queue, and request activity
Policy (/enforcement/policy) protected paths and verification requirements, organization-wide access exceptions, and optional suggestions for other paths
Actions (/enforcement/actions) Response Actions
Setup (/enforcement/setup) saved WAF connections, browser verification, and the edge validator; completed configuration forms open with Manage
History (/enforcement/history) the audit log, and restoring an earlier policy

Old links are forwarded: /response-actions redirects to the Actions sub-page, and bookmarked ?tab= URLs land on the sub-page that replaced them.

A failing readiness check on Overview offers to take you to the control that fixes it, crossing to Setup or Policy as needed, so you never have to work out which of five places owns a problem. The one check that leaves Enforcement entirely is the decoy check, because nothing on this page can fix an off-origin decoy.

History can be filtered by kind of change and narrowed to the last 24 hours, 7 days or 30 days, and rows whose change left a saved configuration offer to restore it. A restore covers policy only: mode, scoped routes with their grades, and the crawler allowlist. Signing keys, machine credentials and the deny-list are never restored, because reinstating those would reverse a security decision somebody made on purpose. See Session Clearance for the full boundary.

The Setup page summarizes what is installed. WAF connection rows name the provider, destination, and site assignment; a connected integration does not mean a blocking rule has been deployed. Completed configuration forms stay closed until you select Manage. Unfinished steps open automatically, and links from readiness checks open the relevant section.

The Cloudflare rule-permission test lives under WAF connections. Browser verification contains the public site key and optional Turnstile settings. The edge validator summary includes its recorded host and latest verified report when available.

The @webdecoy/client script mints the wd_clearance cookie for real browsers during normal browsing. Add it to your site with your site key (shown on Enforcement → Setup, step 2, Browser verificationManage):

<script src="https://cdn.webdecoy.com/client/webdecoy.global.js"
data-site-key="YOUR_SITE_KEY"
async></script>
Attribute Required Description
data-site-key yes Your publishable site key (the organization ID).
data-ingest no WebDecoy ingest origin. Defaults to https://ingest.webdecoy.com.
data-scope no Route-group scope for the token. Defaults to tenant-wide.

Minting is deferred to browser idle time, runs at most once per session, and skips entirely when a fresh cookie already exists. It adds no page-load cost and never blocks paint or interaction.

The validator runs at your CDN edge and fails open on any error: if it can’t reach WebDecoy or can’t verify a token, it forwards the request untouched, so it can never take your site down. Pick the one matching your CDN:

Both validators live in the public WebDecoy/edge repo, and Enforcement → Setup (step 3) pre-fills the config for your organization:

  • Cloudflare: one-click Deploy to Cloudflare installs the Worker into your account; then add a route for your domain using the pre-filled wrangler.toml variables shown in the dashboard.
  • AWS / CloudFront: a Lambda@Edge function on your distribution’s viewer-request behavior: set your keys in src/config.ts, build and zip, upload to S3 in us-east-1, then launch the provided CloudFormation template and attach the published function version to your distribution. Because AWS has no platform-verified-bot signal, this validator verifies crawlers itself via forward-confirmed reverse DNS.

On Enforcement → Setup, step 3 (Deploy the edge validator), enter your site domain and click Verify deployment. The dashboard checks, from your own browser, that the validator is answering on your zone, and reports its current mode. Since the validator fails open, this check is how you confirm it’s actually live before you rely on it. That panel also gives you the pre-filled config (your site key and route already filled in) for both Cloudflare and AWS.

This step is not optional. The token requirement applies only to paths you scope, so an organization in enforce mode with no scoped routes gates nothing: every request passes.

Scoping a path is what stops scrapers on it. A real browser earns a token in the background and never notices. Anything that cannot run JavaScript (curl, scrapers, most AI crawlers) gets a 403 it has no way to complete. This needs no fingerprint, no IP list and no machine learning; it works because the thing being asked for can only be produced by running JavaScript.

Add patterns under Enforcement → Policy, in Protected paths:

Pattern Matches
/checkout/* /checkout and everything beneath it
/api/orders that exact path
/* every path on the site

Start narrow. The paths worth gating are the ones that cost you when abused (checkout, login, search, expensive APIs), not your whole site.

Under the route list, What enforcement isn’t looking at shows the paths your sensors saw that no route covers, sorted into what to do about each:

  • Worth scoping: paths whose shape says close them to automation (/checkout/*, /account/*). One click adds the route.
  • Probes, which should be decoys instead: requests for software you may not run (/wp-admin/, /.env, /vendor/…). Scoping these protects nothing, because nothing is behind them; a decoy on the same path turns every attempt into a detection.
  • Public content, deliberately not suggested: usually your busiest paths. They are listed so you can see they were considered and rejected, not omitted.

Each suggestion shows the token that produced it (matched “/checkout”), because this is a heuristic reading your URL structure and it can be wrong. If something in the probe list is real on your site, scope it instead.

These are two different jobs, and conflating them is how a site loses its search traffic.

Gated routes Public content
What it does Enforcement: non-JS clients get a 403 Detection, impersonation evidence and accounting
Good for login, checkout, APIs, search, exports, anything expensive to serve articles, product pages, docs, and anything else you want indexed
How a crawler is handled blocked unless it is on your verified-bot allowlist observed, classified and counted; never blocked
What you get the abuse stops you learn who took what, and which of them lied about it

On public content the edge validator still annotates every request it forwards, so your origin can act without blocking anyone:

  • x-wd-clearance: the gate’s verdict (valid, missing, verified-bot, signed-agent, …)
  • x-wd-class: what the client is: verified, crawler, script, or browser

Read them with readEdgeVerdict() from @webdecoy/node, getEdgeVerdict() in a Next.js route handler, or the edge.* fields in a WordPress filter rule. That lets you meter, log or cheapen a response for scripted traffic without serving anyone a 403.

Leave enforcement in monitor mode to see the pass/challenge split before anything is actually blocked, then flip to enforce once it looks right.

In monitor mode every request is forwarded with x-wd-clearance set to the verdict enforce would have reached. You can read that from your logs, or from your application:

import { readEdgeVerdict } from '@webdecoy/node';
const edge = readEdgeVerdict(headers);
// edge.clearance === 'missing' → enforce mode would have challenged this request

The status card at the top of the Enforcement Overview shows the current mode and configured path patterns. “Enforcement is on — no protected paths” means enforcement is enabled but no paths have been selected, so requests pass through.

The Enforcement overview shows the actual Protected paths, current mode, and validator status. Expand Setup complete to inspect individual checks; checks that need attention include a link to the relevant setup control.

Request activity reports the selected site’s activity over the last seven days. The exact start and end times are shown in UTC. Counts reflect the paths and mode configured when requests arrived, not just today’s settings.

Metric Meaning
Requests checked Reported requests matching protected paths, in both Monitor and Enforce periods
Requests blocked Requests refused while Enforce mode was active
Would have blocked Requests that failed checks during Monitor mode but were allowed through
Requests on other paths Requests outside protected paths; access rules were not evaluated

Requests on other paths can include intentionally public content. Use Review protected paths to change coverage. Request counts may be incomplete because validators can stop before sending a report.

Expand Reporting details for the report count and any excluded activity. Older or unidentified installations can produce outcomes that cannot be verified or attributed to the selected site. Those outcomes are excluded from the site counts; historical exclusions do not establish the current protection status. Check the validator status in Setup if reporting needs attention.

Organization activity separately shows clearance requests refused across all sites for the same period. This counts token issuance attempts, not unique actors or requests blocked on the selected site.

When no verified activity has been reported, the page directs you to validator status rather than implying that zero activity proves protection. A failed activity read is shown as unavailable.

A token is not simply valid or invalid. It carries a grade reflecting how much positive human evidence the session presented when it was minted:

Grade Earned by Who can reach it
clean any real browser that hasn’t tripped deception everyone
human-likely one positive human signal: a browser-integrity check, interaction cadence, or browser-like TLS everyone
attested-human a passed Turnstile or Apple Private Access Token challenge anyone you’ve configured attestation for

A route can require a minimum grade, so / accepts any valid token while /checkout/* demands human-likely or higher. A session below the bar is challenged there and passes everywhere else.

The minimum is a control on the route, not an API call. Every row under Enforcement → Policy, in Protected paths, shows a Verification required selector. Expand Add a protected path to create a rule. Each option states what it costs a real visitor rather than naming a grade and leaving you to guess:

Verification required Token grade What it asks of a visitor
Browser check (default) clean A valid browser clearance token
Human signals human-likely A clearance token with a positive human signal
Verified challenge attested-human A verified challenge response; configure Turnstile in Setup first

The labels do not change the underlying API values. Access exceptions shows the crawler categories allowed through across all sites, with service credentials in a separate expandable section. Review other paths contains traffic-based suggestions; these do not become rules until you add them.

Changing a grade writes in place and is recorded as its own kind of audit entry, separate from adding a route, because the blast radii differ: raising a bar can start refusing visitors who were passing a moment ago, on a path that was already gated.

Four properties are deliberate:

  • Absent means clean. Tokens minted before grading existed, and validators too old to understand it, keep working exactly as before. Routes you scoped earlier are unaffected until you set a grade on them.
  • Evidence only ever raises a grade. A session that presents none (a keyboard-only visitor, someone on an older browser) mints the same clean token it always did. Nothing here can block a request on its own.
  • A bot-like threat score caps the grade. attested-human needs a threat score of 40 or below and human-likely 60 or below, so positive evidence cannot lift a token whose actor is already known to be bot-like. A real device can still be driving an attack, and conflicting evidence wins.
  • Validators that predate graded trust ignore the minimum and enforce presence alone, so raising it is safe to roll out before every edge is redeployed.

For automation, the same two fields are available over the API. A new route carries its grade on creation; changing an existing one is a PATCH, because re-posting a pattern that already exists returns the stored row untouched and would report success while nothing moved:

POST /organizations/{organization_id}/enforcement/routes
{ "pattern": "/checkout/*", "min_trust": "human-likely" }
PATCH /organizations/{organization_id}/enforcement/routes/{route_id}
{ "min_trust": "attested-human" }

Enforcement keys on two values, and they do different jobs:

Identity What it is What it enforces
JA4 the TLS handshake fingerprint, set by the client’s software and not by whoever wrote the User-Agent fingerprint rules pushed to your WAF
Clearance fingerprint the identity inside a wd_clearance token, derived from stable browser characteristics deny-at-mint, which survives IP rotation

Both are returned on a detection and can be copied from its flyout, along with the IP address and ASN, so arming your WAF is not manual transcription off the screen. When a detection carries its own clearance fingerprint, that value is used in preference to the actor’s rolled-up one: the rollup holds whatever is minting tokens now, while the detection holds what arrived with the request you are looking at.

  • The wd_clearance cookie is a first-party cookie on your domain, which is why the decoy → deny loop needs a same-origin decoy. See the caution at the top, where this belongs rather than in a notes list.
  • The device fingerprint uses stable browser characteristics (canvas, WebGL, screen, timezone, platform, language). Privacy browsers that randomize canvas will simply re-mint each visit, and enforcement still holds within a session.
  • No User-Agent is used in the fingerprint, so a browser update doesn’t churn a user’s identity.