# Gotchas & Troubleshooting ## Execution Order **Problem:** Rules execute in unexpected order **Cause:** Misunderstanding phase execution **Solution:** Phases execute sequentially (can't be changed): 1. `http_request_firewall_custom` - Custom rules 2. `http_request_firewall_managed` - Managed rulesets 3. `http_ratelimit` - Rate limiting Within phase: top-to-bottom, first match wins (unless `skip`) ```typescript // WRONG: Can't mix phase-specific actions await client.rulesets.create({ phase: 'http_request_firewall_custom', rules: [ { action: 'block', expression: 'cf.waf.score gt 50' }, { action: 'execute', action_parameters: { id: 'managed_id' } }, // WRONG ], }); // CORRECT: Separate rulesets per phase await client.rulesets.create({ phase: 'http_request_firewall_custom', rules: [...] }); await client.rulesets.create({ phase: 'http_request_firewall_managed', rules: [...] }); ``` ## Expression Errors **Problem:** Syntax errors prevent deployment **Cause:** Invalid field/operator/syntax **Solution:** ```typescript // Common mistakes 'http.request.path' → 'http.request.uri.path' // Correct field 'ip.geoip.country eq US' → 'ip.geoip.country eq "US"' // Quote strings 'http.user_agent eq "Mozilla"' → 'lower(http.user_agent) contains "mozilla"' // Case sensitivity 'matches ".*[.jpg"' → 'matches ".*\\.jpg$"' // Valid regex ``` Test expressions in Security Events before deploying. ## Skip Rule Pitfalls **Problem:** Skip rules don't work as expected **Cause:** Misunderstanding skip scope **Solution:** Skip types: - `ruleset: 'current'` - Skip remaining rules in current ruleset only - `phases: ['phase_name']` - Skip entire phases ```typescript // WRONG: Trying to skip managed rules from custom phase // In http_request_firewall_custom: { action: 'skip', action_parameters: { ruleset: 'current' }, expression: 'ip.src in {192.0.2.0/24}', } // This only skips remaining custom rules, not managed rules // CORRECT: Skip specific phases { action: 'skip', action_parameters: { phases: ['http_request_firewall_managed', 'http_ratelimit'], }, expression: 'ip.src in {192.0.2.0/24}', } ``` ## Update Replaces All Rules **Problem:** Updating ruleset deletes other rules **Cause:** `update()` replaces entire rule list **Solution:** ```typescript // WRONG: This deletes all existing rules! await client.rulesets.update({ zone_id: 'zone_id', ruleset_id: 'ruleset_id', rules: [{ action: 'block', expression: 'cf.waf.score gt 50' }], }); // CORRECT: Get existing rules first const ruleset = await client.rulesets.get({ zone_id: 'zone_id', ruleset_id: 'ruleset_id' }); await client.rulesets.update({ zone_id: 'zone_id', ruleset_id: 'ruleset_id', rules: [...ruleset.rules, { action: 'block', expression: 'cf.waf.score gt 50' }], }); ``` ## Override Conflicts **Problem:** Managed ruleset overrides don't apply **Cause:** Rule ID doesn't exist or category name incorrect **Solution:** ```typescript // List managed ruleset rules to find IDs const ruleset = await client.rulesets.get({ zone_id: 'zone_id', ruleset_id: 'efb7b8c949ac4650a09736fc376e9aee', }); console.log(ruleset.rules.map(r => ({ id: r.id, description: r.description }))); // Use correct IDs in overrides { action: 'execute', action_parameters: { id: 'efb7b8c949ac4650a09736fc376e9aee', overrides: { rules: [{ id: '5de7edfa648c4d6891dc3e7f84534ffa', action: 'log' }] } } } ``` ## False Positives **Problem:** Legitimate traffic blocked **Cause:** Aggressive rules/thresholds **Solution:** 1. Start with log mode: `overrides: { action: 'log' }` 2. Review Security Events to identify false positives 3. Override specific rules: `overrides: { rules: [{ id: 'rule_id', action: 'log' }] }` ## Rate Limiting NAT Issues **Problem:** Users behind NAT hit rate limits too quickly **Cause:** Multiple users sharing single IP **Solution:** Add more characteristics: User-Agent, session cookie, or authorization header ```typescript { action: 'block', expression: 'http.request.uri.path starts_with "/api"', action_parameters: { ratelimit: { characteristics: ['cf.colo.id', 'ip.src', 'http.request.cookies["session"][0]'], period: 60, requests_per_period: 100, }, }, } ``` ## Performance Issues **Problem:** Increased latency **Cause:** Complex expressions, excessive rules **Solution:** 1. Skip static assets early: `action: 'skip'` for `\\.(jpg|css|js)$` 2. Path-based deployment: Only run managed on `/api` or `/admin` 3. Disable unused categories: `{ category: 'wordpress', enabled: false }` 4. Prefer string operators over regex: `starts_with` vs `matches` ## Limits & Quotas | Resource | Free | Pro | Business | Enterprise | |----------|------|-----|----------|------------| | Custom rules | 5 | 20 | 100 | 1000 | | Rate limiting rules | 1 | 10 | 25 | 100 | | Rule expression length | 4096 chars | 4096 chars | 4096 chars | 4096 chars | | Rules per ruleset | 75 | 75 | 400 | 1000 | | Managed rulesets | Yes | Yes | Yes | Yes | | Rate limit characteristics | 2 | 3 | 5 | 5 | **Important Notes:** - Rules execute in order; first match wins (except skip rules) - Expression evaluation stops at first `false` in AND chains - `matches` regex operator is slower than string operators - Rate limit counting happens before mitigation ## API Errors **Problem:** API calls fail with cryptic errors **Cause:** Invalid parameters or permissions **Solution:** ```typescript // Error: "Invalid phase" → Use exact phase name phase: 'http_request_firewall_custom' // Error: "Ruleset already exists" → Use update() or list first const rulesets = await client.rulesets.list({ zone_id, phase: 'http_request_firewall_custom' }); if (rulesets.result.length > 0) { await client.rulesets.update({ zone_id, ruleset_id: rulesets.result[0].id, rules: [...] }); } // Error: "Action not supported" → Check phase/action compatibility // 'execute' only in http_request_firewall_managed // Rate limit config only in http_ratelimit phase // Error: "Expression parse error" → Common fixes: 'ip.geoip.country eq "US"' // Quote strings 'cf.waf.score gt 40' // Use 'gt' not '>' 'http.request.uri.path' // Not 'http.request.path' ``` **Tip**: Test expressions in dashboard Security Events before deploying.