Securing WordPress With Cloudflare Firewall Rules: What Actually Breaks
Search for Cloudflare firewall rules for WordPress and you will find a hundred articles offering the same copy-paste list. Block xmlrpc, block wp-login, block a few countries, done.
We have run a formal Cloudflare WAF standard across our managed WordPress fleet for the better part of a year. It is on its fourth revision. Every one of those revisions was forced by something breaking on a live client site, and almost none of the breakages were security failures. They were false positives: rules that worked exactly as written and took a client’s payment webhook, link previews or block editor down with them.
That is the part the copy-paste lists never mention, so that is what this post is about.
Rule evaluation order decides whether your rules run at all
Before any specific rule, understand the order Cloudflare evaluates a request in, because getting this wrong is how a carefully written ruleset ends up doing nothing.
IP Access Rules are evaluated first, ahead of your custom rules. An allow entry there bypasses everything below it. This has two consequences that catch people out constantly.
The first is a testing trap. If you are checking your new rules from an office IP that sits on your account allowlist, your entire ruleset will appear inert. Nothing blocks, nothing challenges, and the natural conclusion is that the rules are broken. They are fine. You are exempt. Always validate from a connection that is definitely not allowlisted, such as mobile data.
The second is a genuine security hole. An IP Access allow scoped to an entire ASN bypasses your whole WAF for every address on that network, across every zone on the account. We inherited exactly this on our own infrastructure: a legacy account-wide allow for an image optimisation service’s hosting provider. It had been added to fix a single broken integration and quietly exempted an entire European datacenter network from every rule we had. Never scope an allow to an ASN.
Skip rules are holes you are choosing to dig
Most rulesets need at least one allow or skip rule, for certificate validation and verified crawlers if nothing else. It is worth being precise about what that costs you.
A skip action does not just skip the next rule. Depending on configuration it can short-circuit the rest of your custom rules, the managed ruleset, rate limiting and bot protection in one go. Anything matching it is talking to your origin unprotected.
Version one of our standard had a skip clause intended to let payment callbacks through, matched on a substring appearing in webhook URLs. It meant any request whose path contained that string anywhere ran with zero protection. Guessing the string is not difficult. It was removed.
A later revision had a subtler version of the same bug. Two clauses skipped the WAF for certificate validation paths, matched on the directory prefix alone. Any file under those prefixes was exempt, including PHP. We found it live on one of our own subdomains: a request to a .php file placed inside a certificate validation directory, from an offshore cloud network, sailing through with the WAF standing down. The fix was to constrain those clauses by request method and file extension, so only the extension-less GET requests that real certificate validation actually uses are exempt.
The general rule we now apply: a skip clause may only match on identity-verified signals, such as a genuinely verified crawler, or on a tightly anchored path with traversal guards. Never a bare substring.
The false positives nobody warns you about
These are the failures that generate support tickets, and every one cost us a client conversation before we found it.
Blocking xmlrpc breaks Jetpack and WooPayments. This is the single most repeated piece of WordPress security advice on the internet, and on a site running Jetpack it is wrong. Jetpack’s sync from WordPress.com is not a Cloudflare-verified bot, so it does not benefit from any good-bot allowance. WooPayments requires that WordPress.com connection to function. Block xmlrpc outright and you have broken a client’s payment processing to stop an attack vector that other rules already cover. Jetpack is also the most commonly client-self-installed plugin in WordPress, so this fires on sites where nobody told you it was in use.
Blocking AI crawlers breaks social link previews. Cloudflare files Meta’s crawlers under the AI Crawler category. Block that category and every Facebook, Messenger, WhatsApp and Instagram preview for the site goes blank: no thumbnail, no title, just a bare URL whenever anyone shares a page. We had this confirmed and fixed on a client site last July, and the client’s report was not “security is too tight,” it was “our Facebook posts look broken.”
Blocking SaaS networks breaks integrations silently. Business automation platforms send server-to-server callbacks. If a rule blocks the sending network, those callbacks fail with no user-visible error. Worse, challenging them instead of blocking does not help, because a server cannot solve a challenge. We removed one major SaaS network from our block list entirely after working this out: it has no rentable compute, so it was never a plausible attack source, and blocking it only killed CRM and transactional email callbacks.
Author enumeration rules will 403 your own editor. The patterns used to enumerate WordPress usernames are the same patterns the block editor uses for its author dropdown and @-mentions, and that wp-admin uses to filter posts by author. Any rule targeting enumeration must be gated on a logged-in session, or the client reports that the editor is broken.
Overbroad file-extension matching blocks real images. A rule that blocks any path containing common backup or log extensions will also block a file named after a bakery, or a photo whose name ends in the word logo. Our host’s stock ruleset makes exactly this mistake. Anchoring matters more than coverage.
What geography filtering actually buys you
Version two of our standard challenged traffic from cloud provider networks as well as filtering by country. It seemed obviously correct: attacks come from rented compute.
Then we audited twelve zones properly. That single clause was responsible for essentially every SaaS integration support ticket we had. Google Search Console’s sitemap validator, monitoring services and client reporting dashboards are all cloud hosted in US regions and were caught by nothing else in the ruleset. It also challenged genuine Australian visitors whose connections routed through cloud infrastructure.
What it caught in return was roughly 0.09% of illegitimate bot traffic that no other rule already covered. We dropped it. The lesson generalises: measure what a clause catches against what it breaks before you keep it, and be honest that most security advice has never been measured at all.
The setting that makes path rules real
If you take one configuration change from this post, take this one.
Cloudflare does not percent-decode the request path unless URL Normalization is turned on for the zone. Leave it off and encoded variants of the paths you are blocking walk straight past every path-based rule you have written. We have audited production zones running a genuinely thoughtful ruleset that was, in practice, decorative for precisely this reason.
Normalization also does not touch the query string and does not strip trailing path information, so rules need to account for both independently.
The honest summary
Writing Cloudflare firewall rules for WordPress is not hard. A competent list will stop the overwhelming majority of automated attack traffic before it reaches PHP, and the Free plan is genuinely sufficient to do it.
The work is everything after that: noticing when a rule quietly breaks a payment callback, knowing which clause to relax without opening a bypass, and re-measuring the clauses you assumed were earning their place. Four revisions in, we are still finding them.
If you would rather that ongoing work was not yours, managed Cloudflare configuration is part of what we do.