Cloudflare WAF Configuration
The Cloudflare WAF evaluates every HTTP request against managed rulesets, custom rules, and rate limiting rules before it reaches your origin. This guide covers standard configuration patterns for production zones.
Managed Rulesets
Cloudflare maintains three managed rulesets that update automatically:
- Cloudflare Managed Ruleset - broad coverage for common vulnerabilities (SQLi, XSS, RCE patterns). Enable this first.
- Cloudflare OWASP Core Rule Set - scoring-based detection aligned to OWASP CRS. Requires sensitivity tuning.
- Cloudflare Exposed Credentials Check - detects login requests using compromised credential pairs.
Enable via API
# List available managed rulesets
curl -s "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/rulesets" \
-H "Authorization: Bearer ${API_TOKEN}" | jq '.result[] | .id, .name, .kind'
# Deploy a managed ruleset
curl -X PUT "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/rulesets/${RULESET_ID}" \
-H "Authorization: Bearer ${API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{"rules": [{"action": "execute", "expression": "true"}]}' OWASP Core Rule Set
The OWASP CRS uses anomaly scoring. Each matching rule adds points. When the total exceeds a threshold, the request is blocked.
Sensitivity levels
- High - threshold 25. Blocks most attacks but generates more false positives. Use for static sites.
- Medium - threshold 40. Good balance for most web applications.
- Low - threshold 60. Fewer false positives. Use when tuning from scratch.
Custom Rules
Custom WAF rules use Cloudflare's expression language (based on Wireshark display filters). Common patterns:
Block known bad paths
# Expression: block common scanner paths
(http.request.uri.path contains "/wp-login.php" and not ip.src in { 192.168.1.0/24 })
or (http.request.uri.path contains "/.env")
or (http.request.uri.path contains "/xmlrpc.php") Country-based restrictions for admin paths
# Expression: block admin access from outside allowed countries
(http.request.uri.path contains "/admin" and not ip.geoip.country in { "AT" "DE" "CH" }) Header-based rules
# Expression: block requests without a valid user-agent
(not http.user_agent ne "" and http.request.method eq "POST") Rate Limiting
Rate limiting rules apply per-client thresholds on specific endpoints. Priority targets:
- Login endpoints - 5-10 requests per minute per IP
- API endpoints - depends on expected usage, start conservative
- Form submissions - 3-5 requests per minute per IP
- Password reset - 3 requests per hour per IP
# Expression: rate limit login attempts
(http.request.uri.path eq "/api/auth/login" and http.request.method eq "POST")
# Action: Block
# Period: 60 seconds
# Requests: 10
# Mitigation timeout: 600 seconds Exception Management
WAF exceptions (skip rules) should be scoped as narrowly as possible:
- Skip by specific rule ID, not entire rulesets
- Scope to specific URI paths or request characteristics
- Document the reason for each exception
- Review exceptions quarterly - remove any that are no longer needed
Testing Changes
Before deploying WAF changes to production:
- Deploy rules in log mode first (action: Log)
- Monitor matched requests in Security Analytics for 24-48 hours
- Verify no legitimate traffic is being matched
- Switch to block mode (action: Block or Challenge)
- Keep the old rule config exported for rollback
# Export current WAF rules (for rollback)
curl -s "https://api.cloudflare.com/client/v4/zones/${ZONE_ID}/rulesets" \
-H "Authorization: Bearer ${API_TOKEN}" | jq '.' > waf-rules-backup.json