Clearance Enforcement
What it is
Section titled “What it is”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.
How a request is decided
Section titled “How a request is decided”At the edge, each request passes if any of these hold, checked in order:
- Machine service token: a credential you issued for a partner, monitor, or job (see Response Actions).
- Verified bot: Googlebot, Bingbot, AI crawlers, and monitoring services you’ve allowed (verified by the platform / reverse-DNS, never by User-Agent).
- Unscoped route: the path isn’t in your token-enforced route list, so no token is required.
- Valid
wd_clearancecookie: 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-clearanceheader 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.
1. Add the client script
Section titled “1. Add the client script”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 verification → Manage):
<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.
2. Deploy the edge validator
Section titled “2. Deploy the edge validator”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.tomlvariables 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 inus-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.
3. Verify the deployment
Section titled “3. Verify the deployment”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.
4. Scope the routes you want gated
Section titled “4. Scope the routes you want gated”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.
Not sure what to scope?
Section titled “Not sure what to scope?”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.
Gated routes vs public content
Section titled “Gated routes vs public content”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, orbrowser
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.
5. Roll out in monitor mode first
Section titled “5. Roll out in monitor mode first”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 requestThe 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.
Reading request activity
Section titled “Reading request activity”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.
Trust levels
Section titled “Trust levels”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.
Setting the minimum
Section titled “Setting the minimum”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
cleantoken it always did. Nothing here can block a request on its own. - A bot-like threat score caps the grade.
attested-humanneeds a threat score of 40 or below andhuman-likely60 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" }The two identities enforcement acts on
Section titled “The two identities enforcement acts on”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.
Privacy and stability notes
Section titled “Privacy and stability notes”- The
wd_clearancecookie 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.