Cloudflare & Security

How We Configure Cloudflare’s WAF Differently for WordPress and Astro

Steven Dey Steven Dey 5 min read Updated 22 July 2026
Isometric illustration of two distinct shields representing different Cloudflare WAF profiles for WordPress and Astro

We put every site behind Cloudflare, but the actual WAF configuration is not the same file copied onto every zone. A WordPress site and an Astro site have almost nothing in common in terms of what needs protecting, and treating them identically means either leaving gaps on the WordPress side or blocking legitimate traffic on the Astro side.

WordPress: the attack surface is the admin layer and the endpoints attackers already know

A WordPress install has a small number of predictable, high-value targets: wp-login.php, xmlrpc.php, wp-admin, and the REST API routes under /wp-json/. Our WAF rules for WordPress zones are built around exactly these paths.

A typical custom rule expression looks something like: (http.request.uri.path contains "/wp-login.php" and not ip.src in $known_admin_ips), paired with a rate-limiting rule capping login attempts per IP to a handful per minute rather than blocking outright, since a single legitimate admin fat-fingering a password should never get locked out. XML-RPC gets blocked entirely by default with a simple (http.request.uri.path eq "/xmlrpc.php") rule, unless a specific integration, usually a mobile publishing app or Jetpack, actually needs it, in which case we scope the exception to that one use case rather than leaving the endpoint open for everyone.

REST API write endpoints get a tighter rule again: GET requests to /wp-json/ are generally left alone since plenty of legitimate front-end functionality depends on them, but POST, PUT, and DELETE methods against those routes get restricted to authenticated, expected origins. We also deploy Cloudflare’s managed ruleset tuned for known WordPress core and plugin CVEs, since that is where the overwhelming majority of real compromise attempts actually originate, not from novel zero-days but from scanners checking version strings against a public vulnerability database. None of this is exotic. It is closing the specific doors that get targeted, rather than a generic “security mode” toggle.

Astro: there is no admin layer to protect, so the rules do different work

A static Astro site has no login page, no database, and no plugin code executing per request, so most of the WordPress-specific rules above are irrelevant by construction. There is nothing at /wp-admin to protect because nothing exists there.

What the Astro WAF profile focuses on instead is the surface that does exist: API routes under /api/, the contact and support form handlers built with @astrojs/cloudflare. These get their own rate-limiting rule, typically capping POST requests per IP per minute, tuned specifically to stop form-spam and scripted abuse rather than the credential-stuffing patterns a WordPress login page attracts. Bot Fight Mode gets tuned differently too: on a WordPress zone we lean toward more aggressive challenge behaviour since the admin surface is worth protecting even at the cost of occasionally challenging a legitimate but unusual visitor, while on an Astro zone serving entirely static, publicly cacheable content, we keep it lighter so it does not interfere with legitimate crawlers indexing the site. The risk profile shifts from “protect the admin surface” to “protect the handful of dynamic endpoints,” which is a much smaller and more precisely defined job.

Cache rules are the other half of the split, and they run in the opposite direction

WordPress zones need careful cache exclusions: /wp-admin/*, /wp-login.php, and any URL carrying a logged-in cookie must bypass cache entirely, or an admin can end up looking at a cached page meant for a different user. Astro zones invert this. Since the entire public site is static HTML generated at build time, we cache almost everything aggressively at the edge, often with cache TTLs measured in hours rather than minutes, because the content genuinely does not change between deploys. Getting this backwards in either direction causes real problems: an under-cached Astro site loses most of the performance benefit of being static in the first place, and an over-cached WordPress site starts serving stale or wrong content to logged-in users.

Headless WordPress plus Astro needs both profiles, pointed at two different zones

Where a site runs WordPress as a headless content source behind an Astro frontend, as this blog does, the WAF configuration splits across two zones with two different jobs. The WordPress zone (blog.shadowtek.com.au in our own case) keeps the full WordPress-hardening profile above, because the admin dashboard and GraphQL endpoint still exist and still need protecting, just no longer facing the public. The Astro zone gets the lighter, API-focused profile, because it is only ever reading published content, never writing to the database. Getting this split wrong in either direction either leaves the WordPress backend under-protected or throttles the read-only frontend for no reason.

What stays constant across every zone

A handful of settings apply everywhere regardless of stack: TLS 1.2 minimum, Always Use HTTPS, HSTS with a sensible max-age, and Cloudflare’s own managed ruleset for known CVEs and malicious bot signatures. Security level and browser integrity checks stay on for every zone too, since they cost nothing in legitimate traffic and catch a meaningful slice of low-effort automated abuse before it reaches either stack. These are the baseline, not the differentiator. The actual work of hardening a site properly is in the zone-specific rules layered on top, tuned to what that particular stack can and cannot be attacked through.

Why this matters more than it looks like it should

The failure mode we see most often when a site moves from WordPress to a headless Astro frontend is someone copying the WordPress zone’s WAF configuration onto the new Astro zone unchanged, on the theory that “the security settings are already dialled in, why touch them.” That leaves rate limits tuned for a login page sitting on top of a site that has no login page, while the API routes that do exist stay under-protected because nothing in the original ruleset was written with them in mind. Treating the two stacks identically is not a safe default. It is a specific kind of gap that only shows up once someone actually goes looking for it. We cover the WordPress side of this in more detail in the security gaps we keep finding in client audits, and the full breakdown of the stack sits on our Cloudflare & Security page.