The policy → rule → condition model
- Policy: belongs to one threat (fake accounts, account takeover, SMS pumping) and one
event type (
signup,login,otp). Rules inside it apply only to traces of that event. - Rule: has a name, a position, an action, a logic (
AND/OR), and one or more conditions. - Condition: a single test:
field operator value. Operators areeq,gt,lt. - First-match-wins: rules are evaluated top to bottom by position. The first rule whose
conditions are all satisfied determines the verdict. If no rule matches, the verdict is
allow(implicit).
Opening the rule editor
In the dashboard, go to Guard. Each policy card shows its rules. Click Add rule to create a new one, or click an existing rule to edit it.
Rule editor: open with conditions and action
eq / gt / lt), value field, the AND/OR toggle, and the action buttons (challenge / deny).
Creating a rule
1
Name the rule
Give the rule a descriptive name. It appears in the event feed when a trace matches, so
make it readable:
Block credential stuffing, Challenge TOR logins, Flag disposable email.2
Add conditions
Each condition has three parts:
See Signals for the complete list of fields you can use.Click + Add condition to add more. Use the AND / OR toggle to control logic:
- AND: all conditions must match (default; use for precision).
- OR: any condition is enough (use for catch-all rules like “any automation signal”).
3
Choose an action
There is no explicit
allow action. If no rule matches, the request is allowed implicitly.4
Set the position
Position controls evaluation order. Lower numbers run first. Drag rules to reorder them, or
type a position number. Put your most specific, high-confidence rules first so they don’t
get shadowed by broad catch-alls.
5
Save
Click Save rule. The rule is added to the policy immediately. In Monitor mode you’ll
start seeing matching traces without any user impact.
Conditions in detail
Field reference
Conditions reference signal fields from the/trace response. See Signals
for the authoritative list. A few common ones:
Operators
AND vs OR logic
A rule’s logic applies across all its conditions:First-match-wins ordering
Rules are evaluated top to bottom. The first rule that fully matches wins; lower rules are not evaluated. This means:- Put narrow, high-confidence
denyrules above broadchallengerules. - A
denyat position 1 prevents achallengeat position 2 from ever running on the same request. - An
allow-equivalent (no rule) only applies when every rule fails to match.
Monitor vs Enforce mode
Rules themselves don’t have a mode. The Cloudflare integration route rule has the mode (configured per route in Cloudflare → Routes). The distinction:
Use Monitor to validate your rules against real traffic before switching to Enforce.
Setting the blocked response
When a route is in Enforce mode and adeny verdict is issued, the Worker returns a response
you configure. Set it per-route in the Cloudflare integration settings:
- Status code (default
403) - 302 redirect: send users to a
/blockedpage - Custom HTML: a branded error page returned to browsers
- Custom JSON: an error object returned to API/XHR clients
Worked examples
Challenge VPN logins
Challenge VPN logins
Policy: account-takeover / login
Action:
challenge | Logic: AND | Position: 1Sends any login from a VPN to step-up (MFA/CAPTCHA) in your app. Lower false-positive
rate than blocking outright, since many legitimate users use VPNs.Block credential stuffing
Block credential stuffing
Policy: account-takeover / login
Action:
deny | Logic: AND | Position: 1Blocks an IP that has already failed 5+ login attempts in the last hour and is currently
failing again. Stops credential stuffing loops cold.This uses
event.status eq "failed" so it only triggers when the current attempt also
failed, so you don’t block a legitimate user whose IP happened to share a /24 with a bot.
For the outcome status to be accurate, send it in the server-side call.Block automation at signup
Block automation at signup
Policy: fake-accounts / signup
Action:
deny | Logic: OR | Position: 1Blocks signups where the device is a headless browser or shows automation signals. Using
OR catches both Playwright/Puppeteer runs (is_headless) and more subtle scripts
(automation_detected).Deny disposable email signups
Deny disposable email signups
Policy: fake-accounts / signup
Action:
deny | Logic: AND | Position: 2Hard-blocks signups with throwaway email addresses. Position 2 because the automation
rule at position 1 is more severe and should run first.Block OTP pumping: rate limit
Block OTP pumping: rate limit
Policy: SMS-pumping / otp
Action:
deny | Logic: AND | Position: 1Blocks any IP that fires more than 5 OTP requests in 5 minutes. This is the telltale pattern of
a pumping script hammering your OTP send endpoint.Challenge new device + successful login
Challenge new device + successful login
Policy: account-takeover / login
Action:
challenge | Logic: AND | Position: 3Triggers step-up when a user successfully logs in from a device tracenow has never seen
for their account. Requires the server-side call to supply the
real subject_user_id (the edge has no user context).Related
Signals
Every field you can use in conditions, with types and example values.
Verdicts
What allow / challenge / deny mean and how to act on them.
Cloudflare integration
Route rules, Monitor vs Enforce mode, and the blocked response editor.
Server-side integration
Add the in-app call to unlock per-user signals like new device and impossible travel.
