☁️ Cloud Security June 15, 2025 · 10 min read

Cloudflare WAF Rule Expressions: 25 Production Examples

Cloudflare WAF rule expressions explained — http.request.uri.path syntax, geographic blocks, bot mitigation, rate limiting. Production-ready snippets you can paste.

CS
☁️ Cloud Security
CS

A Web Application Firewall sits between your users and your application, inspecting every HTTP request and blocking those that match known attack patterns or custom rules. For complete protection, pair WAF with a managed SOC to detect and respond to threats that bypass WAF controls. For most internet-facing web applications, a properly configured WAF is the most cost-effective security control you can deploy — stopping the bulk of automated attacks before they ever reach your application code.

Our cloud security team deploys and tunes Cloudflare WAF configurations as part of a broader cloud hardening programme. See how we implemented this for a Melbourne SaaS company in our Cloudflare WAF case study.

Cloudflare is the dominant WAF provider for good reason: global network (300+ PoPs), DDoS mitigation integrated at the network layer, aggressive threat intelligence updates, and a freemium model that makes enterprise-grade protection accessible to organisations of any size. This guide takes you from zero to a hardened, operationally mature Cloudflare WAF deployment.


25 Production-Ready Cloudflare WAF Rule Expressions

Skip ahead to the syntax reference if you’re here to copy a working expression. Each rule below pastes directly into Cloudflare → Security → WAF → Custom rules (or → Rate limiting rules where noted). Every expression has been deployed in production engagements.

Path-based blocks (the most common pattern)

1. Block exact path match

(http.request.uri.path eq "/admin")

Use for one-off URLs that should never be public. Comparison is case-sensitive; normalise with lower() if needed.

2. Block path prefix

(starts_with(http.request.uri.path, "/api/internal/"))

The starts_with function is faster than regex and the right tool for a prefix lookup. Use lower(http.request.uri.path) if your application normalises case server-side.

3. Block multiple paths in one rule

(http.request.uri.path in {"/wp-admin" "/wp-login.php" "/.env" "/.git/config"})

The in operator with a set literal is the cleanest way to bundle related blocks. Far better than chaining or clauses — keeps the rule readable and the evaluation fast.

4. Block path with regex (when needed)

(http.request.uri.path matches "^/api/v[12]/internal/.*$")

Use regex only when prefix/suffix matchers don’t fit. Anchoring with ^ and $ is critical — without anchors, the pattern matches anywhere in the path.

5. Allow specific paths only (block everything else)

(not starts_with(http.request.uri.path, "/api/public/") and
 not starts_with(http.request.uri.path, "/health") and
 http.request.uri.path ne "/")

Default-deny pattern for internal services exposed via Cloudflare. Inverse logic — list what’s allowed, block the rest.

Method + body restrictions

6. Block non-idempotent methods on read-only paths

(starts_with(http.request.uri.path, "/api/public/") and
 http.request.method in {"POST" "PUT" "PATCH" "DELETE"})

Useful when a public read endpoint shouldn’t accept writes regardless of auth state.

7. Block requests with no Authorization header on protected paths

(starts_with(http.request.uri.path, "/api/private/") and
 not any(http.request.headers.names[*] eq "authorization"))

The any() over http.request.headers.names[*] is the canonical pattern for header existence checks.

8. Block oversized request bodies

(http.request.body.size gt 1048576)

1 MB ceiling. Combine with path prefix for targeted limits per endpoint.

Geographic + ASN filtering

9. Block by country list

(ip.geoip.country in {"RU" "KP" "IR"})

Cloudflare’s GeoIP is updated weekly. Two-letter ISO 3166-1 alpha-2 codes.

10. Allow only specific countries

(not ip.geoip.country in {"US" "GB" "AU" "DE" "IN" "SG"})

Inverse pattern — useful for B2B SaaS targeting specific markets.

11. Block by ASN

(ip.geoip.asnum in {16509 14618 14061})

Useful for blocking cloud providers (AWS = 16509, Amazon also 14618, DigitalOcean = 14061). Be careful — legitimate customers route through these too. Use as a challenge, not an outright block.

Bot management

12. Block low-score bots (paid Bot Management plan)

(cf.bot_management.score lt 30 and not cf.bot_management.verified_bot)

Score below 30 = high-confidence bot. Whitelist verified_bot (Googlebot, Bingbot) to avoid SEO damage.

13. Block static-asset hotlinking

(starts_with(http.request.uri.path, "/static/") and
 not http.referer contains "yourdomain.com" and
 http.referer ne "")

Blocks third parties embedding your images while allowing direct-load and empty-referer requests (RSS readers, some browsers).

14. Challenge requests with no User-Agent

(http.user_agent eq "")

Bots often send empty UA. Use js_challenge action to filter without blocking legitimate edge cases.

Headers + JWT inspection

15. Block requests with suspicious header injection attempts

(any(http.request.headers.values[*] contains "\r\n") or
 any(http.request.headers.values[*] contains "\n"))

Catches header injection / response splitting attempts.

16. Block JWT path-traversal in Authorization header

(any(http.request.headers["authorization"][*] contains "..") or
 any(http.request.headers["authorization"][*] contains "/etc/passwd"))

Defense-in-depth — your app should reject these too.

Rate limiting expressions

17. Per-IP rate limit on login endpoint

Match expression:  (http.request.uri.path eq "/login" and http.request.method eq "POST")
Characteristics:   IP address
Period:            10 seconds
Requests:          5
Action:            Block (or Managed Challenge)

Stops credential stuffing without affecting normal users.

18. Per-API-key rate limit

Match expression:  (starts_with(http.request.uri.path, "/api/v1/"))
Characteristics:   http.request.headers["x-api-key"]
Period:            60 seconds
Requests:          1000
Action:            Block

Per-key limits enforce SLA tiers without an application-side rate limiter.

Defensive patterns

19. Block SSRF probes (gopher / file / dict protocols in query strings)

(lower(http.request.uri.query) contains "gopher://" or
 lower(http.request.uri.query) contains "file://" or
 lower(http.request.uri.query) contains "dict://")

Catches naive SSRF payloads at the edge.

20. Block AWS metadata-IP exfiltration attempts

(http.request.uri.query contains "169.254.169.254" or
 http.request.uri.query contains "metadata.google.internal" or
 http.request.uri.query contains "metadata.azure.com")

Pair with application-side SSRF protections — Cloudflare alone isn’t sufficient.

21. Skip WAF for known-good IPs

(ip.src in {203.0.113.10 203.0.113.11 198.51.100.0/24})

Use action: SkipAll WAF rules. Whitelist office IPs, CI runners, monitoring probes. Apply BEFORE block rules.

22. Block dangerous file extensions

(http.request.uri.path matches "\\.(env|git|svn|bak|backup|sql|sh|key|pem|p12)$")

Prevents accidental backup-file exposure. Run this even if you don’t think you have such files — accidental deploys happen.

23. Block requests with empty Host header

(http.host eq "")

Often a scanner probing for misconfiguration. Real browsers always send Host.

24. Block direct origin access (force Cloudflare proxy)

(http.host ne "yourdomain.com" and ip.src ne 173.245.48.0/20)

Apply at the origin via separate firewall. Stops attackers from bypassing Cloudflare by hitting your origin IP directly.

25. Challenge ICMP / non-HTTP-resembling requests

(http.request.method not in {"GET" "POST" "PUT" "PATCH" "DELETE" "HEAD" "OPTIONS"})

Catches scanner traffic using unusual methods.

Operational gotchas

  • lower() for case-insensitive matching — Cloudflare expressions are case-sensitive by default. Wrap paths and headers with lower() if your application is case-insensitive.
  • Order matters — Cloudflare evaluates rules top-to-bottom. Put Skip rules (whitelists) before block rules.
  • Test with log action first — for any new rule, set the action to Log for 48 hours. Check Security Events for false positives before flipping to Block.
  • http.request.body.* requires the Inspect Body setting — under Security → WAF → Settings. Not enabled by default.
  • Rate limiting rules and Custom rules are evaluated independently — a request matched by a rate limiting rule can still be processed by Custom rules.

For deeper coverage of the Cloudflare expression language, see the official Cloudflare Rules language reference. The patterns above are the ones I find myself reaching for on most engagements.


Understanding What a WAF Protects Against

Before configuration, understand what a WAF does and doesn’t do:

WAF protects against:

  • Injection attacks (SQL injection, command injection) via HTTP requests
  • Cross-site scripting (XSS)
  • SSRF (Server-Side Request Forgery) via known patterns
  • Path traversal
  • Remote file inclusion
  • Automated scanners and vulnerability crawlers
  • Application-layer DDoS (HTTP flood)
  • Credential stuffing on login endpoints
  • Bad bots and scrapers

WAF does NOT protect against:

  • Logic flaws in your application (a WAF can’t understand your business rules)
  • Vulnerabilities in your server configuration outside the request path — address these with vulnerability management
  • Supply chain attacks (compromised dependencies)
  • Authenticated attackers who have valid sessions
  • Zero-day exploits with no known signature

A WAF is defence in depth, not a substitute for secure development. It buys you time and blocks the noise — but your application needs to be coded securely as well. A cloud security assessment can identify the configuration gaps that a WAF alone won’t address.


Cloudflare Plan Tiers

FeatureFreePro ($20/mo)Business ($200/mo)Enterprise
Managed WAF rulesBasicFull OWASP CRSFull + custom page rulesFull + custom + priority support
Custom WAF rules5201001000+
Rate limiting1 rule10 rulesUnlimitedUnlimited
Bot managementBasic Bot Fight ModeSuper Bot Fight ModeSuper Bot Fight ModeAdvanced Bot Management
DDoS protectionUnmetered (L3/4/7)UnmeteredUnmeteredUnmetered + custom thresholds
Analytics24hr72hr30 days90 days
Log forwardingLogpushLogpush

For most production web applications, Pro or Business is the minimum recommended tier. The Business tier unlocks full custom rules and Logpush (essential for SIEM integration).


Phase 1: DNS and Proxy Configuration

1.1 Onboard Your Domain

  1. Add your domain to Cloudflare
  2. Update your domain’s nameservers to Cloudflare’s
  3. Wait for propagation (typically 1–24 hours)

1.2 Enable Cloudflare Proxy

In DNS settings, ensure the orange cloud (proxy) is enabled for all records that serve web traffic. Grey cloud = DNS only, no WAF/DDoS protection.

A   api.example.com   → 1.2.3.4   [Proxied ✓]
A   www.example.com   → 1.2.3.4   [Proxied ✓]
A   mail.example.com  → 1.2.3.4   [DNS only — don't proxy mail]

1.3 Lock Down Your Origin

Once behind Cloudflare, your origin server should only accept traffic from Cloudflare’s IP ranges — otherwise attackers bypass your WAF by hitting the origin directly.

Cloudflare IPv4 ranges (as of writing — always check the live list at cloudflare.com/ips):

173.245.48.0/20
103.21.244.0/22
103.22.200.0/22
103.31.4.0/22
141.101.64.0/18
108.162.192.0/18
190.93.240.0/20
188.114.96.0/20
197.234.240.0/22
198.41.128.0/17
162.158.0.0/15
104.16.0.0/13
104.24.0.0/14
172.64.0.0/13
131.0.72.0/22

AWS security group (restrict inbound HTTP/HTTPS to Cloudflare only):

# Get Cloudflare IPs dynamically
CF_IPS=$(curl -s https://www.cloudflare.com/ips-v4)

# Add each to your security group
for cidr in $CF_IPS; do
  aws ec2 authorize-security-group-ingress \
    --group-id sg-xxxx \
    --protocol tcp --port 443 \
    --cidr $cidr
done

Also implement Cloudflare Authenticated Origin Pulls — your origin verifies that requests come from Cloudflare using mTLS:

# nginx — verify Cloudflare client cert
ssl_client_certificate /etc/ssl/cloudflare-origin-pull-ca.pem;
ssl_verify_client on;

Phase 2: WAF Rule Configuration

2.1 Enable Managed Rules (OWASP Core Rule Set)

Navigate to Security → WAF → Managed Rules. Enable:

  1. Cloudflare Managed Ruleset — Cloudflare’s proprietary ruleset, updated continuously based on threat intelligence
  2. Cloudflare OWASP Core Ruleset — implements OWASP CRS 3.x

Initial action: Set all rules to Log mode first. Do not block immediately — spend 1-2 weeks reviewing logs to identify false positives before switching to Block.

OWASP Paranoia Level: Start at level 1 (low false positives). Increase to level 2 or 3 as you tune out false positives.

2.2 WAF Rule Actions

ActionBehaviourWhen to Use
LogRecord but allowInitial deployment, tuning phase
BlockReturn 403Confirmed malicious patterns
ChallengeShow CAPTCHASuspicious but uncertain
JS ChallengeBrowser integrity check (silent)Bot traffic
Managed ChallengeCloudflare decides typeDefault for most rules
SkipBypass WAF for this requestTrusted IPs, known-good traffic

2.3 Custom WAF Rules

After enabling managed rules, add custom rules tailored to your application.

Block requests with no User-Agent:

(not http.request.headers["user-agent"][0] exists)
→ Block

Block common scanner signatures:

(http.request.headers["user-agent"][0] contains "sqlmap") or
(http.request.headers["user-agent"][0] contains "nikto") or
(http.request.headers["user-agent"][0] contains "masscan") or
(http.request.headers["user-agent"][0] contains "zgrab")
→ Block

Block path traversal attempts:

(http.request.uri.path contains "../") or
(http.request.uri.path contains "..%2F") or
(http.request.uri.path contains "%2e%2e%2f")
→ Block

Protect admin paths — challenge non-office IPs:

(http.request.uri.path starts_with "/admin") and
(not ip.src in {203.0.113.0/24 198.51.100.0/24})
→ Managed Challenge

Block credential stuffing on login — challenge high request volume:

(http.request.uri.path eq "/api/auth/login") and
(http.request.method eq "POST")
→ Rate limit (see Phase 3)

Geo-blocking (if you don’t serve certain countries):

(ip.geoip.country in {"CN" "RU" "KP" "IR"})
→ Block

Use this carefully — it’s a blunt instrument and blocks legitimate users. Useful when you’re seeing specific attack traffic from regions you don’t serve.


Phase 3: Rate Limiting

Cloudflare rate limiting lets you define rules based on request volume from an IP or across a session.

Login Endpoint Protection

Rule name: Login Rate Limit
Expression: (http.request.uri.path eq "/api/auth/login" and http.request.method eq "POST")
Threshold: 5 requests per 60 seconds per IP
Action: Block for 10 minutes

API Endpoint General Protection

Rule name: API Rate Limit
Expression: (http.request.uri.path starts_with "/api/")
Threshold: 300 requests per 60 seconds per IP
Action: Managed Challenge

Password Reset / Account Recovery Protection

Rule name: Password Reset Rate Limit
Expression: (http.request.uri.path contains "/forgot-password" or
             http.request.uri.path contains "/reset-password")
Threshold: 3 requests per 300 seconds per IP
Action: Block for 1 hour

GraphQL Query Depth Limiting

For GraphQL APIs, limit query complexity to prevent resource exhaustion:

Expression: (http.request.uri.path eq "/graphql" and
             http.request.body.size > 10000)
Action: Block

Phase 4: Bot Management

4.1 Cloudflare Bot Fight Mode

For Pro plans: Enable Super Bot Fight Mode under Security → Bots.

Options:

  • Definitely automated → Block or Challenge
  • Likely automated → Challenge
  • Verified bots (Google, Bing, monitoring services) → Allow

4.2 Advanced Bot Management (Enterprise)

Enterprise Bot Management adds:

  • Bot score (0–99) — challenge or block based on confidence level
  • Machine learning-based bot detection
  • JavaScript fingerprinting
  • Tor exit node detection
  • Anomaly-based detection for sophisticated bots

Custom rule using bot score:

(cf.bot_management.score lt 30 and
 not http.request.uri.path starts_with "/api/webhook/")
→ Block

4.3 Handling False Positives for Legitimate Bots

Some automated traffic is legitimate — monitoring services, SEO crawlers, uptime monitors. Allowlist known-good IPs:

(ip.src in {185.93.228.0/24})  ← Pingdom
→ Skip WAF

Or use the Verified Bots classification to automatically allow Google, Bing, and other major search crawlers.


Phase 5: DDoS Configuration

Cloudflare’s DDoS protection is always on and unmetered at all plan levels. Key configuration options:

HTTP DDoS Attack Protection

Under Security → DDoS → HTTP DDoS attack protection:

  • Sensitivity: Start at Medium. Increase to High or High+ if you’re under attack.
  • Action: Override to Block for critical endpoints; leave Managed for general traffic.

Network-Layer DDoS (Magic Transit / Spectrum)

For volumetric attacks targeting non-HTTP ports or raw IP infrastructure, Magic Transit (BGP advertisement of your IP space through Cloudflare) or Spectrum (proxied TCP/UDP) extends protection beyond HTTP.


Phase 6: SSL/TLS Configuration

Encryption Mode

Set encryption mode to Full (Strict) — this validates Cloudflare’s connection to your origin server uses a valid certificate.

Free → Flexible (Cloudflare ↔ browser encrypted; Cloudflare ↔ origin unencrypted) ← DON'T USE
     → Full (encrypted to origin, cert not validated) ← Acceptable short-term
     → Full (Strict) (encrypted to origin, valid cert required) ← RECOMMENDED

HSTS

Enable HSTS under SSL/TLS → Edge Certificates:

  • Max-Age: 12 months minimum (31,536,000 seconds)
  • Include subdomains: Yes (if all subdomains are HTTPS)
  • Preload: Yes (submit to HSTS preload list after stable deployment)

Minimum TLS Version

Set minimum TLS to 1.2 (disable TLS 1.0 and 1.1): SSL/TLS → Edge Certificates → Minimum TLS Version → TLS 1.2


Phase 7: Security Headers and Page Rules

Add security headers via Transform Rules → Modify Response Header:

Rule: Add Security Headers
Expression: (true)  ← applies to all responses
Add headers:
  X-Content-Type-Options: nosniff
  X-Frame-Options: DENY
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: geolocation=(), microphone=()
  Content-Security-Policy: default-src 'self'; ...

Phase 8: Logging and SIEM Integration

Cloudflare Logpush (Business/Enterprise)

Set up Logpush to send WAF logs to your SIEM, S3, or SIEM in real-time:

# Create Logpush job to S3
curl -X POST "https://api.cloudflare.com/client/v4/zones/{zone_id}/logpush/jobs" \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  --data '{
    "destination_conf": "s3://my-bucket/cloudflare-logs?region=ap-south-1",
    "dataset": "http_requests",
    "logpull_options": "fields=ClientIP,ClientRequestMethod,ClientRequestURI,WAFAction,WAFRuleID,EdgeResponseStatus&timestamps=rfc3339",
    "enabled": true
  }'

Key fields to capture for security:

  • ClientIP — source IP
  • WAFAction — block/challenge/log
  • WAFRuleID — which rule fired
  • WAFRuleMessage — description of the rule
  • ClientRequestURI — target path
  • ClientRequestMethod — HTTP method
  • EdgeResponseStatus — HTTP response code

Microsoft Sentinel Integration

Cloudflare has a native Sentinel data connector (Community). Alternatively, route Logpush through Azure Event Hub → Log Analytics workspace.

KQL query — top blocked IPs last 24 hours:

Cloudflare_CL
| where WAFAction_s == "block"
| where TimeGenerated > ago(24h)
| summarize BlockCount = count() by ClientIP_s
| top 20 by BlockCount

Tuning and False Positive Management

Process for Tuning

  1. Log mode first — run all rules in Log mode for 2 weeks
  2. Identify false positives — look for legitimate traffic matching rules
  3. Add exceptions — create WAF rule exceptions for specific paths or IPs
  4. Switch to Block — move rules to Block mode once false positives are eliminated
  5. Monitor weekly — review logs for new false positives after application changes

Creating Rule Exceptions

Rule: Exception for API upload endpoint
Expression: (http.request.uri.path starts_with "/api/upload" and
             http.request.method eq "POST")
Managed rules to skip: [specific rule IDs that fire on file uploads]

Never skip entire managed rulesets — be surgical about which rules to exempt.


Security Incident Response with Cloudflare

When you’re under attack:

  1. Enable Under Attack Mode — Security → Settings → Security Level → Under Attack (adds JS challenge to all visitors)
  2. Create emergency IP block — Custom Rule to block attacker IPs immediately
  3. Increase rate limit sensitivity — reduce thresholds on targeted endpoints
  4. Enable firewall events export — get real-time attack telemetry
  5. Contact Cloudflare support — Enterprise accounts get direct DDoS response support

Go-Live Checklist

Before moving all rules from Log to Block:

  • All managed rules reviewed, false positives identified and excepted
  • Custom rules deployed and tested with legitimate traffic
  • Rate limiting configured for login, API, password reset endpoints
  • Origin locked to Cloudflare IPs only
  • Authenticated Origin Pulls configured
  • SSL mode set to Full (Strict)
  • TLS 1.2 minimum enforced
  • HSTS enabled
  • Security headers deployed via Transform Rules
  • Logpush configured and verified
  • Alerts set up for WAF blocks and DDoS events

CyberneticsPlus deploys and tunes Cloudflare WAF configurations for clients across SaaS, financial services, and e-commerce as part of our cloud security practice. We handle full deployment, custom rule development, SIEM integration, and ongoing tuning. Read our Cloudflare WAF Melbourne SaaS case study or contact us to protect your web applications.

#Cloudflare #WAF #web application firewall #DDoS protection #bot management #security hardening

Related articles

Need expert help with Cloud Security?

Our certified security team is ready to assess your environment and recommend the right solutions.

Book a Free Consultation