Skip to content
Go back

Cloudflare WAF: Free Tier Firewall Rules

By SumGuy 7 min read
Cloudflare WAF: Free Tier Firewall Rules

You’re Already Paying for This — Stop Ignoring It

You set up Cloudflare, pointed your DNS at it, and called it a day. Orange cloud icon? Enabled. Traffic proxied? Sure. Security configured? …default settings. Sound familiar?

Here’s the thing: Cloudflare’s free tier ships with a genuinely useful WAF, rate limiting, bot protection, and always-on DDoS mitigation. Most homelab owners and small site operators leave all of it at factory defaults and wonder why wp-login.php is getting hammered at 3 AM.

Let’s fix that.


What the Free Tier Actually Gives You

Before you dive in, know what you’re working with:

What you don’t get free: Cloudflare’s managed rulesets (OWASP, WordPress, etc.), ML-based anomaly detection, custom IP lists beyond 10 entries, and advanced rate limiting with multiple conditions. Paid tiers start at $20/month if you ever need those.

For a homelab blog, self-hosted app, or small business site? Five well-written rules and rate limiting will cover 90% of the garbage you’ll see in the wild.


Custom Firewall Rules: The Expression Builder

Head to Security → WAF → Custom rules in your Cloudflare dashboard. Each rule has:

  1. A match expression — what traffic to target
  2. An action — what to do when it matches

Actions available on free tier:

ActionWhat It Does
BlockHard 403. Done.
JS ChallengeServes a JavaScript puzzle. Bots usually fail.
Managed ChallengeCloudflare decides: invisible CAPTCHA, JS challenge, or pass. Smart default.
ChallengeOld-school CAPTCHA. Avoid unless you hate your users.
SkipBypass other rules. Useful for allowlisting.
LogRecord only. Good for testing before blocking.

Managed Challenge is usually the right call for suspicious-but-not-certain traffic. It’s invisible to real humans and annoying to bots.


Practical Rules Worth Writing

Block WordPress Attack Probes

If you’re not running WordPress, there’s zero reason to let scanners probe these endpoints.

Block WP attack endpoints
(http.request.uri.path contains "/wp-login.php") or
(http.request.uri.path contains "/xmlrpc.php") or
(http.request.uri.path contains "/wp-admin") and
(not ip.src in {YOUR_HOME_IP/32 YOUR_VPN_IP/32})

Action: Block

If you are running WordPress, swap Block for Managed Challenge on wp-login.php and keep your own IPs allowlisted via the Skip action with higher priority.

Block Known Bad User Agents

Scanners, vulnerability tools, and lazy scrapers announce themselves in the User-Agent header. Take them up on the offer.

Block scanner user agents
(http.user_agent contains "sqlmap") or
(http.user_agent contains "nikto") or
(http.user_agent contains "nmap") or
(http.user_agent contains "masscan") or
(http.user_agent contains "zgrab") or
(http.user_agent eq "")

Action: Block

The empty user agent catch (http.user_agent eq "") is a nice bonus — real browsers always send one.

Geo-Restrict Admin Areas

If your admin panel only needs to be accessible from your country (or a short list of countries), enforce it.

Restrict admin to allowed countries
(http.request.uri.path contains "/admin") and
(not ip.geoip.country in {"US" "CA"})

Action: Block or Managed Challenge depending on your paranoia level.

Challenge High-Threat Countries for the Whole Site

You can dial up friction for countries you’re not targeting without fully blocking them.

Challenge high-threat regions
(ip.geoip.country in {"CN" "RU" "KP" "IR"}) and
(not cf.client.bot)

Action: Managed Challenge

The not cf.client.bot condition exempts Cloudflare’s own verified bots (Googlebot, etc.) from the challenge. Without it, you’d break crawling.


Bot Fight Mode: Good, With Caveats

Enable it: Security → Bots → Bot Fight Mode → On

Bot Fight Mode uses Cloudflare’s fingerprinting to identify and block automated traffic. It’s effective against dumb scanners and credential stuffing attacks.

The catch: it catches legitimate bots too. Specifically:

If you’re getting false positives, check Security → Events to see what’s being challenged and whether it matters. You can write a Skip rule for specific IPs (your uptime monitor’s IPs) with higher priority than Bot Fight Mode.


Rate Limiting: Protect Login Endpoints and APIs

Free tier gives you one rate limiting rule. Use it on your most-abused endpoint.

Security → WAF → Rate limiting rules → Create rule

A good default for a login endpoint:

Rate limit login endpoint
http.request.uri.path eq "/login"

That’s 5 attempts per 10 seconds before a 1-hour block. Aggressive enough to stop brute force, permissive enough that a human who mistyped their password twice won’t get locked out for an hour.

For an API endpoint you want to throttle:

That’s 1 req/sec sustained — reasonable for most API consumers, limiting for scrapers.


Security Levels: Global Sensitivity

Security → Settings → Security Level

This is a global dial that controls how aggressive Cloudflare is with visitors who have bad IP reputation scores. Options:

Default is Medium. For a homelab or personal site with no business need to be maximally accessible: High is fine. You’ll challenge more legitimate users (your logs will show it), but the noise drop is real.

Don’t use “I’m Under Attack” as a permanent setting. It breaks RSS readers, API clients, and anything that doesn’t run JavaScript.


Page Shield: Basic JS Supply Chain Protection

Security → Page Shield

Page Shield monitors the third-party scripts loading on your pages. On the free tier, it logs what scripts are detected and alerts on new ones. It won’t block them automatically (that’s paid), but it’s a useful early warning for supply chain attacks.

Enable it and let it run for a week. Then audit the list. If you see something unexpected — a CDN you don’t recognize, a script path you didn’t add — investigate.


Cloudflare Tunnels + WAF: The Full Picture

If you’re running Cloudflare Tunnels (cloudflared) to expose services without opening ports, the WAF still applies. Traffic hits Cloudflare’s edge first, goes through your firewall rules and Bot Fight Mode, and only reaches your tunnel if it passes.

Your origin is never exposed. The WAF rules you write above protect the tunnel endpoint just as they’d protect any proxied domain. This is the setup: origin fully hidden, WAF in front, rate limiting on sensitive paths.


The Five Rules I’d Write First

If you only have five slots and you’re not sure where to start:

  1. Block scanner user agents (empty + known tool strings)
  2. Block wp-login.php and xmlrpc.php (unless you’re actually running WP and you’ve allowlisted your IP)
  3. Geo-restrict your admin panel to your country + your VPN IP
  4. Challenge known high-threat geos on the main site
  5. Rate limit your login or API endpoint

That’s it. Five rules, maybe 20 minutes of setup, and you’ve gone from “default Cloudflare” to “actually defended.”

Your 3 AM self will appreciate it.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it may appear here.


Previous Post
Alpine vs. Distroless: Choosing Your Minimalist Base
Next Post
Distroless: How to Build Slim, Secure Containers

Related Posts