update skills

This commit is contained in:
2026-03-17 16:53:22 -07:00
parent 0b0783ef8e
commit f9a530667e
389 changed files with 54512 additions and 1 deletions

View File

@@ -0,0 +1,113 @@
# Cloudflare WAF Expert Skill Reference
**Expertise**: Cloudflare Web Application Firewall (WAF) configuration, custom rules, managed rulesets, rate limiting, attack detection, and API integration
## Overview
Cloudflare WAF protects web applications from attacks through managed rulesets and custom rules.
**Detection (Managed Rulesets)**
- Pre-configured rules maintained by Cloudflare
- CVE-based rules, OWASP Top 10 coverage
- Three main rulesets: Cloudflare Managed, OWASP CRS, Exposed Credentials
- Actions: log, block, challenge, js_challenge, managed_challenge
**Mitigation (Custom Rules & Rate Limiting)**
- Custom expressions using Wirefilter syntax
- Attack score-based blocking (`cf.waf.score`)
- Rate limiting with per-IP, per-user, or custom characteristics
- Actions: block, challenge, js_challenge, managed_challenge, log, skip
## Quick Start
### Deploy Cloudflare Managed Ruleset
```typescript
import Cloudflare from 'cloudflare';
const client = new Cloudflare({ apiToken: process.env.CF_API_TOKEN });
// Deploy managed ruleset to zone
await client.rulesets.create({
zone_id: 'zone_id',
kind: 'zone',
phase: 'http_request_firewall_managed',
name: 'Deploy Cloudflare Managed Ruleset',
rules: [{
action: 'execute',
action_parameters: {
id: 'efb7b8c949ac4650a09736fc376e9aee', // Cloudflare Managed Ruleset
},
expression: 'true',
enabled: true,
}],
});
```
### Create Custom Rule
```typescript
// Block requests with attack score >= 40
await client.rulesets.create({
zone_id: 'zone_id',
kind: 'zone',
phase: 'http_request_firewall_custom',
name: 'Custom WAF Rules',
rules: [{
action: 'block',
expression: 'cf.waf.score gt 40',
description: 'Block high attack scores',
enabled: true,
}],
});
```
### Create Rate Limit
```typescript
await client.rulesets.create({
zone_id: 'zone_id',
kind: 'zone',
phase: 'http_ratelimit',
name: 'API Rate Limits',
rules: [{
action: 'block',
expression: 'http.request.uri.path eq "/api/login"',
action_parameters: {
ratelimit: {
characteristics: ['cf.colo.id', 'ip.src'],
period: 60,
requests_per_period: 10,
mitigation_timeout: 600,
},
},
enabled: true,
}],
});
```
## Managed Ruleset Quick Reference
| Ruleset Name | ID | Coverage |
|--------------|----|---------|
| Cloudflare Managed | `efb7b8c949ac4650a09736fc376e9aee` | OWASP Top 10, CVEs |
| OWASP Core Ruleset | `4814384a9e5d4991b9815dcfc25d2f1f` | OWASP ModSecurity CRS |
| Exposed Credentials Check | `c2e184081120413c86c3ab7e14069605` | Credential stuffing |
## Phases
WAF rules execute in specific phases:
- `http_request_firewall_managed` - Managed rulesets
- `http_request_firewall_custom` - Custom rules
- `http_ratelimit` - Rate limiting rules
- `http_request_sbfm` - Super Bot Fight Mode (Pro+)
## Reading Order
1. **[api.md](api.md)** - SDK methods, expressions, actions, parameters
2. **[configuration.md](configuration.md)** - Setup with Wrangler, Terraform, Pulumi
3. **[patterns.md](patterns.md)** - Common patterns: deploy managed, rate limiting, skip, override
4. **[gotchas.md](gotchas.md)** - Execution order, limits, expression errors
## See Also
- [Cloudflare WAF Docs](https://developers.cloudflare.com/waf/)
- [Ruleset Engine](https://developers.cloudflare.com/ruleset-engine/)
- [Expression Reference](https://developers.cloudflare.com/ruleset-engine/rules-language/)

View File

@@ -0,0 +1,202 @@
# API Reference
## SDK Setup
```typescript
import Cloudflare from 'cloudflare';
const client = new Cloudflare({
apiToken: process.env.CF_API_TOKEN,
});
```
## Core Methods
```typescript
// List rulesets
await client.rulesets.list({ zone_id: 'zone_id', phase: 'http_request_firewall_managed' });
// Get ruleset
await client.rulesets.get({ zone_id: 'zone_id', ruleset_id: 'ruleset_id' });
// Create ruleset
await client.rulesets.create({
zone_id: 'zone_id',
kind: 'zone',
phase: 'http_request_firewall_custom',
name: 'Custom WAF Rules',
rules: [{ action: 'block', expression: 'cf.waf.score gt 40', enabled: true }],
});
// Update ruleset (include rule id to keep existing, omit id for new rules)
await client.rulesets.update({
zone_id: 'zone_id',
ruleset_id: 'ruleset_id',
rules: [
{ id: 'rule_id', action: 'block', expression: 'cf.waf.score gt 40', enabled: true },
{ action: 'challenge', expression: 'http.request.uri.path contains "/admin"', enabled: true },
],
});
// Delete ruleset
await client.rulesets.delete({ zone_id: 'zone_id', ruleset_id: 'ruleset_id' });
```
## Actions & Phases
### Actions by Phase
| Action | Custom | Managed | Rate Limit | Description |
|--------|--------|---------|------------|-------------|
| `block` | ✅ | ❌ | ✅ | Block request with 403 |
| `challenge` | ✅ | ❌ | ✅ | Show CAPTCHA challenge |
| `js_challenge` | ✅ | ❌ | ✅ | JS-based challenge |
| `managed_challenge` | ✅ | ❌ | ✅ | Smart challenge (recommended) |
| `log` | ✅ | ❌ | ✅ | Log only, don't block |
| `skip` | ✅ | ❌ | ❌ | Skip rule evaluation |
| `execute` | ❌ | ✅ | ❌ | Deploy managed ruleset |
### Phases (Execution Order)
1. `http_request_firewall_custom` - Custom rules (first line of defense)
2. `http_request_firewall_managed` - Managed rulesets (pre-configured protection)
3. `http_ratelimit` - Rate limiting (request throttling)
4. `http_request_sbfm` - Super Bot Fight Mode (Pro+ only)
## Expression Syntax
### Fields
```typescript
// Request properties
http.request.method // GET, POST, etc.
http.request.uri.path // /api/users
http.host // example.com
// IP and Geolocation
ip.src // 192.0.2.1
ip.geoip.country // US, GB, etc.
ip.geoip.continent // NA, EU, etc.
// Attack detection
cf.waf.score // 0-100 attack score
cf.waf.score.sqli // SQL injection score
cf.waf.score.xss // XSS score
// Headers & Cookies
http.request.headers["authorization"][0]
http.request.cookies["session"][0]
lower(http.user_agent) // Lowercase user agent
```
### Operators
```typescript
// Comparison
eq // Equal
ne // Not equal
lt // Less than
le // Less than or equal
gt // Greater than
ge // Greater than or equal
// String matching
contains // Substring match
matches // Regex match (use carefully)
starts_with // Prefix match
ends_with // Suffix match
// List operations
in // Value in list
not // Logical NOT
and // Logical AND
or // Logical OR
```
### Expression Examples
```typescript
'cf.waf.score gt 40' // Attack score
'http.request.uri.path eq "/api/login" and http.request.method eq "POST"' // Path + method
'ip.src in {192.0.2.0/24 203.0.113.0/24}' // IP blocking
'ip.geoip.country in {"CN" "RU" "KP"}' // Country blocking
'http.user_agent contains "bot"' // User agent
'not http.request.headers["authorization"][0]' // Header check
'(cf.waf.score.sqli gt 20 or cf.waf.score.xss gt 20) and http.request.uri.path starts_with "/api"' // Complex
```
## Rate Limiting Configuration
```typescript
{
action: 'block',
expression: 'http.request.uri.path starts_with "/api"',
action_parameters: {
ratelimit: {
// Characteristics define uniqueness: 'ip.src', 'cf.colo.id',
// 'http.request.headers["key"][0]', 'http.request.cookies["session"][0]'
characteristics: ['cf.colo.id', 'ip.src'], // Recommended: per-IP per-datacenter
period: 60, // Time window in seconds
requests_per_period: 100, // Max requests in period
mitigation_timeout: 600, // Block duration in seconds
counting_expression: 'http.request.method ne "GET"', // Optional: filter counted requests
requests_to_origin: false, // Count all requests (not just origin hits)
},
},
enabled: true,
}
```
## Managed Ruleset Deployment
```typescript
{
action: 'execute',
action_parameters: {
id: 'efb7b8c949ac4650a09736fc376e9aee', // Cloudflare Managed
overrides: {
// Override specific rules
rules: [
{ id: '5de7edfa648c4d6891dc3e7f84534ffa', action: 'log', enabled: true },
],
// Override categories: 'wordpress', 'sqli', 'xss', 'rce', etc.
categories: [
{ category: 'wordpress', enabled: false },
{ category: 'sqli', action: 'log' },
],
},
},
expression: 'true',
enabled: true,
}
```
## Skip Rules
Skip rules bypass subsequent rule evaluation. Two skip types:
**Skip current ruleset**: Skip remaining rules in current phase only
```typescript
{
action: 'skip',
action_parameters: {
ruleset: 'current', // Skip rest of current ruleset
},
expression: 'http.request.uri.path ends_with ".jpg" or http.request.uri.path ends_with ".css"',
enabled: true,
}
```
**Skip entire phases**: Skip one or more phases completely
```typescript
{
action: 'skip',
action_parameters: {
phases: ['http_request_firewall_managed', 'http_ratelimit'], // Skip multiple phases
},
expression: 'ip.src in {192.0.2.0/24 203.0.113.0/24}',
enabled: true,
}
```
**Note**: Skip rules in custom phase can skip managed/ratelimit phases, but not vice versa (execution order).

View File

@@ -0,0 +1,203 @@
# Configuration
## Prerequisites
**API Token**: Create at https://dash.cloudflare.com/profile/api-tokens
- Permission: `Zone.WAF Edit` or `Zone.Firewall Services Edit`
- Zone Resources: Include specific zones or all zones
**Zone ID**: Found in dashboard > Overview > API section (right sidebar)
```bash
# Set environment variables
export CF_API_TOKEN="your_api_token_here"
export ZONE_ID="your_zone_id_here"
```
## TypeScript SDK Usage
```bash
npm install cloudflare
```
```typescript
import Cloudflare from 'cloudflare';
const client = new Cloudflare({ apiToken: process.env.CF_API_TOKEN });
// Custom rules
await client.rulesets.create({
zone_id: process.env.ZONE_ID,
kind: 'zone',
phase: 'http_request_firewall_custom',
name: 'Custom WAF',
rules: [
{ action: 'block', expression: 'cf.waf.score gt 50', enabled: true },
{ action: 'challenge', expression: 'http.request.uri.path eq "/admin"', enabled: true },
],
});
// Managed ruleset
await client.rulesets.create({
zone_id: process.env.ZONE_ID,
phase: 'http_request_firewall_managed',
rules: [{
action: 'execute',
action_parameters: { id: 'efb7b8c949ac4650a09736fc376e9aee' },
expression: 'true',
}],
});
// Rate limiting
await client.rulesets.create({
zone_id: process.env.ZONE_ID,
phase: 'http_ratelimit',
rules: [{
action: 'block',
expression: 'http.request.uri.path starts_with "/api"',
action_parameters: {
ratelimit: {
characteristics: ['cf.colo.id', 'ip.src'],
period: 60,
requests_per_period: 100,
mitigation_timeout: 600,
},
},
}],
});
```
## Terraform Configuration
```hcl
provider "cloudflare" {
api_token = var.cloudflare_api_token
}
resource "cloudflare_ruleset" "waf_custom" {
zone_id = var.zone_id
kind = "zone"
phase = "http_request_firewall_custom"
rules {
action = "block"
expression = "cf.waf.score gt 50"
}
}
```
**Managed Ruleset & Rate Limiting**:
```hcl
resource "cloudflare_ruleset" "waf_managed" {
zone_id = var.zone_id
name = "Managed Ruleset"
kind = "zone"
phase = "http_request_firewall_managed"
rules {
action = "execute"
action_parameters {
id = "efb7b8c949ac4650a09736fc376e9aee"
overrides {
rules {
id = "5de7edfa648c4d6891dc3e7f84534ffa"
action = "log"
}
}
}
expression = "true"
}
}
resource "cloudflare_ruleset" "rate_limiting" {
zone_id = var.zone_id
phase = "http_ratelimit"
rules {
action = "block"
expression = "http.request.uri.path starts_with \"/api\""
ratelimit {
characteristics = ["cf.colo.id", "ip.src"]
period = 60
requests_per_period = 100
mitigation_timeout = 600
}
}
}
```
## Pulumi Configuration
```typescript
import * as cloudflare from '@pulumi/cloudflare';
const zoneId = 'zone_id';
// Custom rules
const wafCustom = new cloudflare.Ruleset('waf-custom', {
zoneId,
phase: 'http_request_firewall_custom',
rules: [
{ action: 'block', expression: 'cf.waf.score gt 50', enabled: true },
{ action: 'challenge', expression: 'http.request.uri.path eq "/admin"', enabled: true },
],
});
// Managed ruleset
const wafManaged = new cloudflare.Ruleset('waf-managed', {
zoneId,
phase: 'http_request_firewall_managed',
rules: [{
action: 'execute',
actionParameters: { id: 'efb7b8c949ac4650a09736fc376e9aee' },
expression: 'true',
}],
});
// Rate limiting
const rateLimiting = new cloudflare.Ruleset('rate-limiting', {
zoneId,
phase: 'http_ratelimit',
rules: [{
action: 'block',
expression: 'http.request.uri.path starts_with "/api"',
ratelimit: {
characteristics: ['cf.colo.id', 'ip.src'],
period: 60,
requestsPerPeriod: 100,
mitigationTimeout: 600,
},
}],
});
```
## Dashboard Configuration
1. Navigate to: **Security** > **WAF**
2. Select tab:
- **Managed rules** - Deploy/configure managed rulesets
- **Custom rules** - Create custom rules
- **Rate limiting rules** - Configure rate limits
3. Click **Deploy** or **Create rule**
**Testing**: Use Security Events to test expressions before deploying.
## Wrangler Integration
WAF configuration is zone-level (not Worker-specific). Configuration methods:
- Dashboard UI
- Cloudflare API via SDK
- Terraform/Pulumi (IaC)
**Workers benefit from WAF automatically** - no Worker code changes needed.
**Example: Query WAF API from Worker**:
```typescript
export default {
async fetch(request: Request, env: Env): Promise<Response> {
return fetch(`https://api.cloudflare.com/client/v4/zones/${env.ZONE_ID}/rulesets`, {
headers: { 'Authorization': `Bearer ${env.CF_API_TOKEN}` },
});
},
};
```

View File

@@ -0,0 +1,204 @@
# 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.

View File

@@ -0,0 +1,197 @@
# Common Patterns
## Deploy Managed Rulesets
```typescript
// Deploy Cloudflare Managed Ruleset (default)
await client.rulesets.create({
zone_id: 'zone_id',
kind: 'zone',
phase: 'http_request_firewall_managed',
name: 'Cloudflare Managed Ruleset',
rules: [{
action: 'execute',
action_parameters: {
id: 'efb7b8c949ac4650a09736fc376e9aee', // Cloudflare Managed
// Or: '4814384a9e5d4991b9815dcfc25d2f1f' for OWASP CRS
// Or: 'c2e184081120413c86c3ab7e14069605' for Exposed Credentials
},
expression: 'true', // All requests
// Or: 'http.request.uri.path starts_with "/api"' for specific paths
enabled: true,
}],
});
```
## Override Managed Ruleset
```typescript
await client.rulesets.create({
zone_id: 'zone_id',
phase: 'http_request_firewall_managed',
rules: [{
action: 'execute',
action_parameters: {
id: 'efb7b8c949ac4650a09736fc376e9aee',
overrides: {
// Override specific rules
rules: [
{ id: '5de7edfa648c4d6891dc3e7f84534ffa', action: 'log' },
{ id: '75a0060762034b9dad4e883afc121b4c', enabled: false },
],
// Override categories: wordpress, sqli, xss, rce, etc.
categories: [
{ category: 'wordpress', enabled: false },
{ category: 'sqli', action: 'log' },
],
},
},
expression: 'true',
}],
});
```
## Custom Rules
```typescript
await client.rulesets.create({
zone_id: 'zone_id',
kind: 'zone',
phase: 'http_request_firewall_custom',
name: 'Custom WAF Rules',
rules: [
// Attack score-based
{ action: 'block', expression: 'cf.waf.score gt 50', enabled: true },
{ action: 'challenge', expression: 'cf.waf.score gt 20', enabled: true },
// Specific attack types
{ action: 'block', expression: 'cf.waf.score.sqli gt 30 or cf.waf.score.xss gt 30', enabled: true },
// Geographic blocking
{ action: 'block', expression: 'ip.geoip.country in {"CN" "RU"}', enabled: true },
],
});
```
## Rate Limiting
```typescript
await client.rulesets.create({
zone_id: 'zone_id',
kind: 'zone',
phase: 'http_ratelimit',
name: 'Rate Limits',
rules: [
// Per-IP global limit
{
action: 'block',
expression: 'true',
action_parameters: {
ratelimit: {
characteristics: ['cf.colo.id', 'ip.src'],
period: 60,
requests_per_period: 100,
mitigation_timeout: 600,
},
},
},
// Login endpoint (stricter)
{
action: 'block',
expression: 'http.request.uri.path eq "/api/login"',
action_parameters: {
ratelimit: {
characteristics: ['ip.src'],
period: 60,
requests_per_period: 5,
mitigation_timeout: 600,
},
},
},
// API writes only (using counting_expression)
{
action: 'block',
expression: 'http.request.uri.path starts_with "/api"',
action_parameters: {
ratelimit: {
characteristics: ['cf.colo.id', 'ip.src'],
period: 60,
requests_per_period: 50,
counting_expression: 'http.request.method ne "GET"',
},
},
},
],
});
```
## Skip Rules
```typescript
await client.rulesets.create({
zone_id: 'zone_id',
kind: 'zone',
phase: 'http_request_firewall_custom',
name: 'Skip Rules',
rules: [
// Skip static assets (current ruleset only)
{
action: 'skip',
action_parameters: { ruleset: 'current' },
expression: 'http.request.uri.path matches "\\.(jpg|css|js|woff2?)$"',
},
// Skip all WAF phases for trusted IPs
{
action: 'skip',
action_parameters: {
phases: ['http_request_firewall_managed', 'http_ratelimit'],
},
expression: 'ip.src in {192.0.2.0/24}',
},
],
});
```
## Complete Setup Example
Combine all three phases for comprehensive protection:
```typescript
const client = new Cloudflare({ apiToken: process.env.CF_API_TOKEN });
const zoneId = process.env.ZONE_ID;
// 1. Custom rules (execute first)
await client.rulesets.create({
zone_id: zoneId,
phase: 'http_request_firewall_custom',
rules: [
{ action: 'skip', action_parameters: { phases: ['http_request_firewall_managed', 'http_ratelimit'] }, expression: 'ip.src in {192.0.2.0/24}' },
{ action: 'block', expression: 'cf.waf.score gt 50' },
{ action: 'managed_challenge', expression: 'cf.waf.score gt 20' },
],
});
// 2. Managed ruleset (execute second)
await client.rulesets.create({
zone_id: zoneId,
phase: 'http_request_firewall_managed',
rules: [{
action: 'execute',
action_parameters: { id: 'efb7b8c949ac4650a09736fc376e9aee', overrides: { categories: [{ category: 'wordpress', enabled: false }] } },
expression: 'true',
}],
});
// 3. Rate limiting (execute third)
await client.rulesets.create({
zone_id: zoneId,
phase: 'http_ratelimit',
rules: [
{ action: 'block', expression: 'true', action_parameters: { ratelimit: { characteristics: ['cf.colo.id', 'ip.src'], period: 60, requests_per_period: 100, mitigation_timeout: 600 } } },
{ action: 'block', expression: 'http.request.uri.path eq "/api/login"', action_parameters: { ratelimit: { characteristics: ['ip.src'], period: 60, requests_per_period: 5, mitigation_timeout: 600 } } },
],
});
```