WAF Rules
This page is the full reference for the five built-in WAF rules. For each rule it covers what the rule does, what every option means, what the resulting Cloudflare expression looks like, and the trade-offs to think about when tuning it.
If you just want to get started, see the Quick Start. This page is for when you want to understand exactly what’s happening or troubleshoot a false positive.
How the rules work together#
The five rules are pushed to Cloudflare in a fixed order, and that order matters:
- Rule 1 (Allow Good Bots) uses Cloudflare’s Skip action, which short-circuits the rest of the firewall for matching requests. It runs first so legitimate bots — search engines, monitors, image optimisers — never even reach the blocking rules below.
- Rules 2 and 3 are Block actions. They stop matching requests outright. Block rules go before Challenge rules because once a request is blocked, Cloudflare stops evaluating further rules for it.
- Rules 4 and 5 are Managed Challenge actions. They make the visitor solve a Cloudflare interactive challenge before continuing. Challenges are reserved for traffic that’s suspicious but not definitely bad.
You don’t have to think about this ordering — the plugin handles it for you on every deploy. But it helps to understand it when you’re troubleshooting why a particular request was or wasn’t caught.
All rules are based on the five-rule pattern originally developed by Troy Glancy (WebAgencyHero) and refined by Michael Bourne (URSA6) at wafrules.com.
Rule 1 — Allow Good Bots#
Action: Skip · Description in Cloudflare: [CF WAF] Allow Good Bots
This is the most important rule in the set. It tells Cloudflare which traffic should be allowed through before any blocking rules run. If you forget to whitelist a service you actually use, Rules 2–5 may end up blocking it.
When this rule matches, it skips:
- All remaining custom rules
- All rate limiting rules
- All managed (WAF) rules
- All Super Bot Fight Mode rules
- Zone Lockdown
- User Agent Blocking
- Browser Integrity Check
- Hotlink Protection
- Security Level
In other words, a matching request gets a clean pass through Cloudflare’s entire firewall stack with no further checks. This is exactly what you want for trusted services.
Verified Bot Categories#
At the top of Rule 1 is a multi-select for Cloudflare’s verified bot categories. Cloudflare maintains a curated list of well-behaved bots and tags each one with a category. Allowing a category whitelists every verified bot in it, even ones that don’t exist yet — so as new search engines and services launch, you don’t need to update your rules.
The ten categories enabled by default:
- Accessibility
- Academic Research
- Advertising & Marketing
- Feed Fetcher
- Monitoring & Analytics
- Page Preview
- Search Engine Crawler
- Search Engine Optimization
- Security
- Webhooks
For most sites you should leave all ten on. The only reason to uncheck one is if you want to specifically block a verified bot in that category via Rule 2 — for example, unchecking “Search Engine Crawler” so you can block Yandex in Rule 2 even though Yandex is a Cloudflare-verified bot.
Individual Service Toggles#
Below the categories is a long list of opt-in toggles for specific services that aren’t covered by Cloudflare’s verified bot list. They’re grouped by purpose:
Backups — BackupBuddy, BlogVault, UpdraftPlus
Uptime Monitoring — BetterStack, GTmetrix, Pingdom, StatusCake, UptimeRobot
Image Optimisers — Cloudflare Image Resizing, ExactDN / Easy IO, EWWW, FlyingPress, Imagify, ShortPixel, TinyPNG
SEO Crawlers — Ahrefs, Ahrefs Site Audit, Majestic (MJ12bot), Moz Rogerbot, Screaming Frog, SEMrush, SiteAuditBot, SEMrush OCOB
Security Scanners — SiteLock, Sucuri, VirusTotal, Wordfence
Social Page Previews — Facebook, LinkedIn, Twitter / X
WordPress Management — Jetpack, MainWP, ManageWP, GoDaddy Uptime Monitor, WP Umbrella
These all match by user-agent string, so they’re effective but only as reliable as the user-agent itself. If a service changes its UA, you’ll need to update the plugin to catch the new one.
Let’s Encrypt Verification#
Enabled by default. Adds an exception that lets Let’s Encrypt’s ACME challenge requests through, regardless of where they come from. If your SSL certificate is issued or renewed by Let’s Encrypt (most are), leave this on — without it your renewals can fail.
Custom IP Allowlist#
You can also allowlist specific IPs or CIDR ranges. This is useful for office IPs, your own server’s IP, or services that don’t have a recognisable user-agent. Both IPv4 and IPv6 are accepted, with optional CIDR notation (192.0.2.0/24).
Custom User Agent Allowlist #
In addition to IP addresses, you can allowlist specific user agent strings. This is useful for tools and services that are not on Cloudflare’s verified bot list and don’t have a fixed IP address — SEO audit tools, proprietary crawlers, or internal services that identify themselves by UA.
Enter one user agent string per line. Each entry uses Cloudflare’s http.user_agent contains substring match, so you only need to enter a distinctive part of the string rather than the full UA. For example, entering SiteGuruCrawler will match any request whose user agent contains that string.
To find a blocked user agent: go to Security → Events in your Cloudflare dashboard, find the blocked request, and copy the User Agent value from the event details. Paste it here and redeploy.
These entries are stored separately from the base WAF rules. When a wafrules.com ruleset update is applied, your custom user agents are preserved — no need to re-enter them after an update.
(http.user_agent contains "SiteGuruCrawler") or
(http.user_agent contains "Google-NotebookLM")
As with the IP allowlist, these clauses are appended to the end of Rule 1’s expression at deploy time.
Generated expression (example)#
With default settings, Rule 1’s expression looks roughly like this:
(cf.client.bot) or
(cf.verified_bot_category in {"Accessibility" "Academic Research" ...}) or
(http.user_agent contains "letsencrypt" and http.request.uri.path contains "acme-challenge")
As you tick more services, more (http.user_agent contains "...") clauses are appended.
Rule 2 — Block Aggressive Crawlers & WP Paths#
Action: Block · Description in Cloudflare: [CF WAF] Block Aggressive Crawlers & WP Paths
This rule blocks two categories of bad traffic: hostile crawlers and known WordPress attack paths. Anything matched here is dropped immediately.
Aggressive Search Engines#
These are search engine bots that are sometimes desirable and sometimes not, so they’re off by default. If your audience includes Russia, China, or other regions where these engines are popular, leave them off. If you don’t get traffic from those regions and you want to reduce crawl load, turn them on.
- Yandex
- Sogou
- SEMrush
- Ahrefs
- Baidu
Note: Yandex, Sogou, SEMrush, Ahrefs, and Baidu are Cloudflare verified bots. Rule 1’s verified-category whitelist will let them through regardless of what you set here. To actually block them, you have to uncheck the relevant category in Rule 1 first (e.g. “Search Engine Crawler” or “Search Engine Optimization”) and enable the block here.
Aggressive Crawlers (User-Agent matches)#
Generic crawlers that misbehave or scrape aggressively. Most are on by default:
- Neevabot — defunct search engine that still crawls
- Python Requests — the default UA for Python’s
requestslibrary, almost always automation - Generic “crawl” / “bot” / “spider” in user-agent — catches anything that says it’s a crawler but isn’t a Cloudflare-verified bot. The
not cf.client.botcheck makes sure legitimate bots aren’t caught here.
Security Scanners#
Tools used for active reconnaissance against your site. All on by default:
- Nikto — web vulnerability scanner
- SQLMap — SQL injection scanner
- Masscan — fast port/IP scanner
- Nmap — network mapper
If any of these is hitting your site and isn’t you running a security audit, it’s almost certainly hostile.
WordPress Path Blocks#
URLs that are common attack targets. Defaults are tuned for typical WordPress sites:
- xmlrpc (on by default) —
xmlrpc.phpis a major brute-force vector. Unless you actively use XML-RPC (most modern sites don’t), leave this on. - wp-config (off by default) — blocks any URL containing
wp-config. Off because legitimate paths can sometimes contain this string; turn it on if you’re sure. - wp-json (off by default) — blocks the WP REST API. Do not turn this on if you use Gutenberg, WooCommerce, the WordPress mobile app, or any plugin that uses the REST API. Off by default for that reason.
- install.php (off by default) — blocks
/install.php. Useful on established sites to prevent re-installation attempts. - wlwmanifest (on by default) — Windows Live Writer manifest, only used by long-dead software. Always safe to block.
- readme.html (on by default) — exposes your WordPress version, helping attackers target known vulnerabilities. Block it.
- license.txt (on by default) — same reason as readme.html.
Injection Patterns#
Two pattern groups that catch common attack payloads in URLs:
- SQLi sleep patterns (on by default) — blocks query strings containing
pg_sleep,sleep(,benchmark(,dbms_pipe,waitfor delay, and similar SQL injection time-based payloads. - Path traversal (on by default) — blocks URL-encoded directory traversal attempts like
%2e%2e%2fand%2fetc%2fpasswd.
These are very low false-positive risk and should generally stay on.
Generated expression (example)#
A typical Rule 2 expression looks like:
(http.user_agent contains "python-requests") or
(http.user_agent contains "crawl" and not cf.client.bot) or
(http.user_agent contains "nikto") or
(lower(http.request.uri.path) contains "xmlrpc") or
(lower(http.request.uri.query) contains "sleep(") or
...
Rule 3 — Block or Challenge Web Hosts / TOR#
Action: Block (default) or Managed Challenge — your choice · Description: [CF WAF] Block Web Hosts & TOR
This rule targets traffic from datacenter IP ranges and TOR exit nodes. The reasoning: real human visitors browse from residential ISPs, mobile carriers, or office networks. Traffic from a Linode VPS or a Hetzner dedicated server is almost always automation — and if it’s not declaring itself as a known bot, it’s usually probing for something.
Action toggle#
Rule 3 is the only rule with a configurable action. You can choose:
- Block (default) — drop the request entirely
- Managed Challenge — make the visitor solve a Cloudflare challenge
Block is the right choice for most sites. Use Managed Challenge if you have legitimate users who happen to browse via cloud infrastructure (some power users, security researchers, certain corporate VPNs that route through datacenter ranges).
Datacenter ASNs#
Each datacenter is an individual toggle, all on by default:
- DigitalOcean
- Linode (Akamai)
- Vultr
- Hetzner
- OVH
- Contabo
- Scaleway
- DreamHost
- M247
- Leaseweb
- GoDaddy
- Alibaba
- HostRoyale
- Cloudvider
The expression specifically excludes Cloudflare-verified bots (not cf.client.bot) and Let’s Encrypt ACME challenges, so legitimate services that happen to run on these providers still get through.
TOR#
The Block TOR toggle adds (ip.src.country in {"T1"}) to the expression. T1 is Cloudflare’s special country code for TOR exit nodes. On by default.
Generated expression (example)#
(ip.src.asnum in {14061 63949 20473 24940 ...} and not cf.client.bot and not http.request.uri.path contains "acme-challenge") or
(ip.src.country in {"T1"})
Rule 4 — Challenge Large Providers / Country#
Action: Managed Challenge · Description: [CF WAF] Challenge Large Providers / Country
Where Rule 3 handles smaller datacenter providers (often used by attackers because they’re cheap), Rule 4 handles the big three cloud providers — AWS, Google Cloud, and Azure. These need a softer touch because legitimate services frequently run on them. Instead of blocking, Rule 4 issues a Cloudflare Managed Challenge.
It also includes an optional country-based challenge.
Cloud Provider Challenges#
All three on by default:
- AWS — challenges traffic from Amazon’s main commercial ASNs
- Google Cloud (GCP) — challenges traffic from Google’s cloud ASNs (note: this is cloud infrastructure, not Google Search, which is whitelisted by Rule 1’s verified categories)
- Azure — challenges traffic from Microsoft Azure
Each one excludes Cloudflare-verified bots and major bot categories (Search Engine Crawler, SEO, Monitoring & Analytics, Advertising & Marketing, Page Preview, Academic Research, Security, Accessibility, Webhooks, Feed Fetcher) plus Let’s Encrypt. So legitimate scrapers, monitors, and tools running on AWS still get through — only unidentified automation gets challenged.
Country Challenge (optional)#
A powerful but blunt instrument. When enabled, it challenges any visitor whose country is not in your allowlist.
This is the inverse of country blocking: you’re saying “I do business in these countries, and everything else gets a challenge.” If your customer base is geographically narrow (e.g. a US-only law firm, a Kuwait-only e-commerce site), this can dramatically cut hostile traffic. If you’re global, leave it off.
The country picker supports multi-select. Like the cloud provider rules, it excludes verified bots and Let’s Encrypt so your SEO and monitoring still works internationally.
Generated expression (example)#
(ip.src.asnum in {16509 14618 7224 15169 396982 8075} and not cf.client.bot and not cf.verified_bot_category in {"Search Engine Crawler" ...} and not http.request.uri.path contains "acme-challenge")
With country challenge enabled:
... or (not ip.src.country in {"US"} and not cf.client.bot and not cf.verified_bot_category in {...} and not http.request.uri.path contains "acme-challenge")
Rule 5 — Challenge VPN Connections & wp-login#
Action: Managed Challenge · Description: [CF WAF] Challenge VPN & wp-login
The final rule in the stack. It challenges traffic from known VPN providers and adds extra protection on wp-login.php.
VPN traffic is challenged rather than blocked because plenty of legitimate users browse via VPN — for privacy, work, or to access geo-restricted content. A Managed Challenge lets real humans through with one click while stopping automated brute-force attacks.
VPN Providers#
Each major commercial VPN is an individual toggle. All on by default. There’s also a master Challenge All VPN Providers toggle at the top that flips all of them in one click.
- NordVPN
- ExpressVPN
- PureVPN
- Surfshark
- IPVanish
- QuadraNet
- OVH France
- Mullvad
- PrivateLayer
Some VPN providers share ASNs (for example, ExpressVPN and Surfshark both use M247’s ASN 9009 in some regions), so the plugin deduplicates ASNs before deploying — you won’t get an oversized expression even if you check everything.
wp-login.php Challenge#
The single most useful toggle in the entire plugin. On by default. Adds a Managed Challenge to every request hitting wp-login.php.
This stops virtually all automated WordPress brute-force attacks dead. Real users barely notice — they get a one-click challenge once per session — and your login page becomes a non-target overnight. Unless you have an unusual setup that blocks Cloudflare challenges, leave this on.
Generated expression (example)#
(ip.src.asnum in {207137 141039 147049 9009 16247 51332 ...}) or
(http.request.uri.path contains "wp-login.php")
Viewing live rules#
After deploying, you can confirm what’s currently live on any zone using the View Zone Rules tab on the WAF Manager dashboard. Pick a zone from the dropdown and you’ll see every custom rule on that zone, including ones deployed by other tools or added manually in Cloudflare. Rules deployed by this plugin are tagged [CF WAF] in their description so they’re easy to spot.
You can also see the same view in Cloudflare itself under Security → WAF → Custom rules.
Tweaking and redeploying#
The dashboard always reflects your current saved configuration, not what’s live on a zone. To change a rule:
- Toggle the options on the dashboard.
- Save (if your version requires it — newer versions auto-save).
- Scroll to Deploy Rules to Your Sites and redeploy to the affected zones.
Redeploying replaces the existing rules on each zone with the new versions. There’s no merge — what you see on the dashboard is what gets pushed.
Was this helpful?
Thanks for your feedback!
✓ Feedback received. Thank you!