DEV Community

Cover image for Cloudflare free plan WAF rules I kept after 7 days of scanner logs
HowardZlh
HowardZlh

Posted on Originally published at guushu.com AI-assisted

Cloudflare free plan WAF rules I kept after 7 days of scanner logs

Originally published at guushu.com/notes. I keep the original updated, so this copy may lag.

These are the Cloudflare free plan WAF rules running on my zone today: three custom rules and one rate limiting rule. I got there by reading a week of firewall events, and by undoing one change that blocked my own images.

The short version: path rules do most of the work. The rate limit is where you hurt yourself.

What 7 days of Cloudflare security events looked like

Between September 17 and 24, the zone saw about 34,000 requests. 55% of them hit a security action. The events sample I pulled had 5,208 rows from 920 IPs.

None of it was aimed at me. It was the same background noise every public hostname gets: .env files, .git/config, WordPress logins, a React RCE probe for CVE-2025-55182, empty user agents, Go-http-client.

The part that surprised me was cost. Blocked requests never reach the Worker. My busiest Worker peaked at 786 requests in a day, against a free allowance of 100,000.

To see your own, the GraphQL Analytics API has a firewall events dataset. On Free, it's sampled, and the hourly query budget is small, so don't loop it.

{
  viewer {
    zones(filter: { zoneTag: "<zone id>" }) {
      firewallEventsAdaptive(
        limit: 100
        filter: { datetime_geq: "2026-09-17T00:00:00Z" }
        orderBy: [datetime_DESC]
      ) { action source clientIP clientRequestPath userAgent clientASNDescription }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Is it an attack or background scanning?

Background scanning, in my case. Look at three things: the paths (secret files and CMS logins you don't have), the source network (cloud and VPS ranges, not home ISPs), and the timing (dozens of paths from one IP inside a second). If all three match, it's an automated scanner, and blocking it at the edge is enough.

One warning before you trust a report. The first analysis I got, written by an AI assistant from the same API data, listed /estimate as an unknown endpoint probe. That's my calculator page. It also counted /zh-Hant/ and /sitemap.xml. Check every "attack path" against your own routes.

Two path rules: block WordPress and secret-file paths

Free gives you 5 custom rules and no regex, so every rule is a chain of contains. I use two for paths.

The WordPress one. I don't run WordPress anywhere, so nothing legitimate matches:

(http.request.uri.path contains "/wp-admin") or (http.request.uri.path contains "/wp-includes")
or (http.request.uri.path contains "/wp-content") or (http.request.uri.path contains "/wp-json")
or (http.request.uri.path contains "/xmlrpc.php") or (http.request.uri.path contains "/wp-login")
or (http.request.uri.path contains "/wlwmanifest.xml") or (http.request.uri.path contains "/wordpress/")
or (starts_with(http.request.uri.path, "/wp/"))
Enter fullscreen mode Exit fullscreen mode

The secret-file one is longer. The core of it:

(http.request.uri.path contains "/.env") or (http.request.uri.path contains "/.git/")
or (http.request.uri.path contains "/.aws/") or (http.request.uri.path contains "/.docker/")
or (http.request.uri.path contains "/credentials.yml") or (http.request.uri.path contains "/phpinfo")
or (http.request.uri.path contains "/info.php") or (http.request.uri.path contains "/dump.sql")
or (http.request.uri.path contains "/_profiler")
Enter fullscreen mode Exit fullscreen mode

contains "/.env" already catches .env.production and /api/.env. You don't need a line per variant.

Why bother, if those files don't exist? A path rule blocks the first request. A rate limit only kicks in after the threshold, so a slow scanner walks under it forever.

Security rules page on the zone: three active custom rules for WordPress paths, scanner IPs and curl user agents, and sensitive file paths, plus one active rate limiting rule, each with its event count

That's all four rules on my zone as of 2026-09-28, with the dashboard's event count next to each. IPs are replaced with documentation addresses.

The user agent rule: block tool clients, and pay for it

The other custom rule blocks two known IPs plus any user agent containing curl, Go-http-client, python, scrapy or fasthttp.

It works. It also bit me. My own ops notes said Python and Go clients were allowed. The live rule blocked both, and a bare curl https://guushu.com/ returns 403 today. When I check my own site now, I send a browser user agent.

Before copying this rule, list what calls your zone: webhooks, uptime checks, your own scripts. If any of them use a default library user agent, they're gone. This is the rule most likely to cause a quiet outage.

The one free rate limiting rule, and how it 429'd my own images

Free gets one rate limiting rule, counted per IP, with a fixed 10-second window and a fixed 10-second block.

The AI report suggested dropping mine from 20 to 10 requests per 10 seconds. I did. Three days later, an image-heavy page on another subdomain was returning 429 on its own first screen. AI search crawlers were getting rate-limited too: GPTBot, ClaudeBot, PerplexityBot.

The fix was to stop counting things that aren't worth counting:

((http.host eq "guushu.com") or ends_with(http.host, ".guushu.com"))
and not (http.request.uri.path.extension in {"js" "css" "png" "jpg" "jpeg" "gif" "webp" "avif" "svg" "ico" "woff" "woff2" "ttf" "json" "map" "txt" "xml" "webmanifest"})
and not cf.client.bot
Enter fullscreen mode Exit fullscreen mode

Back at 20 requests. The test: 40 concurrent HTML requests from one IP gave 20 × 200 and 20 × 429. 40 concurrent static requests gave 40 × 200. cf.client.bot is true only for Cloudflare's verified bots.

Does a 429 on Googlebot mean you're blocking Google?

Not necessarily. My logs had 107 rate limit events with a Googlebot user agent. Every one came from Google Cloud customer ranges, not the crawler. Google publishes its crawler IP ranges; check the source IP before you loosen anything. Real Googlebot and Bingbot had zero blocks.

Zone settings I changed, and ones I left off

On the settings side, I moved minimum TLS to 1.2, turned on HSTS, and left Bot Fight Mode, AI Labyrinth and the Free managed ruleset on. The full list with a curl for each is in my Cloudflare zone security checklist.

One caution on HSTS. My header now says includeSubDomains; preload. Preload is hard to take back, so be sure every subdomain serves HTTPS before you add it.

I left three things off. Under Attack Mode puts a challenge in front of every visitor, which is a lot of friction for background noise. mTLS wants a client certificate that none of my visitors have. Hotlink protection solves a problem these scans don't have.

A week later, the numbers I'll look at are simple: whether rate limit events from home ISPs stay near zero (before the fix, about 3 of 69 blocked IPs were residential), and whether my own pages still load in one go.

If you run more than one zone and want this kind of drift flagged daily, there's a one-question form, not a product yet: guard.guushu.com/zone-audit.

Original, with any later corrections: guushu.com/notes/cloudflare-free-plan-waf-rules/

Top comments (0)