Web Application Firewall Engineering

Deploy and tune web application firewalls for production protection. Covers WAF rule sets, false positive management, rate limiting, bot detection, custom rules, and the patterns that protect web applications without blocking legitimate traffic.

A Web Application Firewall (WAF) is a security layer that inspects and filters HTTP traffic between a web application and the internet. It protects against common web attacks—SQL injection, cross-site scripting, file inclusion, and more—by applying rules to incoming requests and outgoing responses. WAF engineering matters when the application is exposed to untrusted clients, especially at scale: serving tens of thousands of concurrent requests, handling API endpoints, or processing sensitive user data. It is essential during deployment, incident response, and when enforcing security policies across microservices.

Rule Configuration and Management

Rules define the behavior of the WAF. They are evaluated in sequence, each rule matching a subset of traffic and applying actions such as blocking, logging, or transforming requests. Rules can be authored in a declarative format, often using JSON or YAML, and deployed via version-controlled configuration files.

A typical rule specifies:

Example rule configuration in rules.yaml:

- id: 100
  name: "Block SQL Injection"
  priority: 10
  conditions:
    - type: "header"
      key: "Content-Type"
      value: "application/json"
    - type: "body"
      pattern: "(?i)\\b(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|JOIN|UNION)\\b"
    - type: "path"
      pattern: "^/api/v[1-9]/"
  actions:
    - type: "block"
      code: 403
      message: "SQL Injection detected"
    - type: "log"
      level: "warning"
      message: "SQLi: {{request.id}}"
    - type: "add-header"
      key: "X-WAF-Blocked"
      value: "SQLi"

The WAF evaluates rules in order. A request with Content-Type: application/json and a body containing SELECT * FROM users matches this rule, triggers the block, logs a warning, and adds a header. If a later rule applies the same action, the effects accumulate.

Common Pitfalls in Rule Matching

Rule Lifecycle and Deployment

Rules are versioned and deployed via CI/CD pipelines. A change to rules.yaml triggers a build that validates syntax and generates a WAF configuration artifact.

$ git add rules.yaml
$ git commit -m "Add rate-limiting rule for /login"
$ git push origin main

The pipeline runs:

- name: Validate WAF rules
  run: |
    python -c "
    import yaml
    with open('rules.yaml') as f:
        config = yaml.safe_load(f)
    for rule in config:
        assert 'id' in rule, 'Every rule must have an ID'
        assert 'priority' in rule, 'Priority must be defined'
        assert 'conditions' in rule and len(rule['conditions']) > 0, 'At least one condition required'
    print('All rules valid')
    "

- name: Deploy WAF config
  run: |
    scp rules.yaml user@waf-prod:/etc/waf/rules.yaml
    systemctl reload waf

Failure modes:

Request and Response Inspection

The WAF inspects every HTTP request and response. It parses headers, body, and query parameters, and can apply transformations before forwarding the request to the backend.

Request Inspection

The WAF processes requests in this order:

  1. Parse request line, headers, and body.
  2. Apply rules based on request metadata.
  3. Modify the request: add, update, or remove headers; rewrite paths; set cookies.
  4. Forward to backend.

Example request:

GET /api/v1/users?role=admin&limit=10 HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36

{
  "name": "Alice",
  "email": "[email protected]"
}

The WAF can:

Response Inspection

After the backend responds, the WAF inspects the response and applies post-processing rules.

Example response:

HTTP/1.1 200 OK
Content-Type: application/json
X-Response-Time: 124ms

{
  "data": [
    { "id": 1, "name": "Alice", "role": "admin" }
  ],
  "pagination": { "next": "/api/v1/users?cursor=1" }
}

The WAF can:

Failure Modes in Inspection

Security Rule Types and Patterns

SQL Injection Protection

A rule detects SQL injection by scanning the request body or URL parameters for keywords.

- id: 200
  name: "Detect SQL Injection"
  priority: 5
  conditions:
    - type: "body"
      pattern: "(?i)\\b(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|JOIN|UNION|CREATE|ALTER|DROP|INDEX|WITH|AS|ORDER|BY|GROUP|HAVING|LIMIT|OFFSET|EXISTS|IN|LIKE|IS|NULL|NOT)\\b"
    - type: "query"
      key: "q"
      pattern: "(?i)\\b(SELECT|INSERT|UPDATE|DELETE|FROM|WHERE|JOIN|UNION)\\b"
  actions:
    - type: "block"
      code: 403
      message: "SQL Injection detected in request"
    - type: "log"
      level: "info"
      message: "SQLi: {{request.query.q}}"

This rule blocks requests with q=SELECT%20*%20FROM%20users and logs the query string.

Cross-Site Scripting (XSS) Protection

The WAF inspects input for JavaScript, HTML, or event handlers.

- id: 300
  name: "Block XSS Payloads"
  priority: 15
  conditions:
    - type: "body"
      pattern: "<(script|iframe|object|embed|input|img|link|style|div|a|button|form|select|option|textarea|nav|header|footer|section|article|aside|h[1-6])[^>]*>(.*?)(<\\1[^>]*>)"
    - type: "query"
      key: "xss"
      value: "1"
  actions:
    - type: "block"
      code: 403
      message: "Cross-Site Scripting detected"
    - type: "add-header"
      key: "X-XSS-Protection"
      value: "1; mode=block"

Rate Limiting and Throttling

The WAF tracks request frequency per source (IP, API key, user ID).

- id: 400
  name: "Rate Limit: API v1"
  priority: 20
  conditions:
    - type: "path"
      pattern: "^/api/v1/"
  actions:
    - type: "rate-limit"
      window: "1m"
      limit: 100
      action: "block"
      code: 429
      message: "Rate limit exceeded"
      header: "X-RateLimit-Limit"
      header-value: "100"
      header: "X-RateLimit-Remaining"
      header-value: "{{remaining}}"

The WAF stores counters in memory or a database. On hit, it returns 429 Too Many Requests with headers:

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 97
Retry-After: 60
Content-Type: application/json

{
  "error": "Rate limit exceeded",
  "details": "Request limit of 100 per minute exceeded"
}

Header and Payload Validation

The WAF validates incoming headers and body structure.

- id: 500
  name: "Validate JWT in Authorization Header"
  priority: 30
  conditions:
    - type: "header"
      key: "Authorization"
      pattern: "^Bearer\\s+([a-zA-Z0-9\\-_]+\\.[a-zA-Z0-9\\-_]+\\.[a-zA-Z0-9\\-_]+)$"
    - type: "header"
      key: "Content-Type"
      value: "application/json"
  actions:
    - type: "validate-jwt"
      key: "Authorization"
      algorithm: "HS256"
      issuer: "https://auth.example.com"
      audience: "api.example.com"
      expected-claims:
        - name: "exp"
          type: "number"
        - name: "sub"
          type: "string"
      error-action:
        - type: "block"
          code: 401
          message: "Invalid or expired JWT"
        - type: "log"
          level: "error"
          message: "JWT validation failed: {{jwt.error}}"

The WAF extracts the JWT from the Authorization header, validates signature, expiration, issuer, and audience. If validation fails, it blocks the request and logs the error.

Failure Modes and Operational Observations

Common WAF Operations

Debugging Traffic

Enable detailed logging and request tracing:


$ systemctl set-environment WAF_LOG_LEVEL=debug
$ systemctl reload waf

# Trace a request
$ curl -v -H "X-Debug: trace" https://api.example.com/api/v1/users?limit=10

The WAF logs:

Monitoring and Alerting

The WAF emits structured logs:

{
  "timestamp": "2023-10-05T12:34:56Z",
  "request_id": "a1b2c3d4",
  "method": "GET",
  "path": "/api/v1/users",
  "ip": "192.168.1.100",
  "rules": [
    {
      "id": 100,
      "name": "Block SQL Injection",
      "matched": true,
      "actions": [
        { "type": "block", "code": 403, "message": "SQL Injection detected" },
        { "type": "log", "level": "warning", "message": "SQLi: a1b2c3d4" },
        { "type": "add-header", "key": "X-WAF-Blocked", "value": "SQLi" }
      ]
    }
  ],
  "metrics": {
    "rule_evaluation_time_ms": 12,
    "request_size_bytes": 234,
    "response_size_bytes": 1245
  }
}

Alerting rules:

Upgrading and Rolling Back

WAF upgrades are atomic. A new configuration is applied, and the WAF switches to it with zero downtime.

# Prepare new configuration
$ cp rules.yaml rules-new.yaml
$ systemctl reload waf

# Verify
$ curl -H "X-Test: verify" https://api.example.com/health
# Success → switch to new config
$ systemctl set-environment WAF_ACTIVE_CONFIG=rules-new.yaml
$ systemctl reload waf

Rollback:

Summary

Web Application Firewall engineering is the practice of defining, deploying, and operating rules that inspect and secure HTTP traffic. It is not a static configuration but a dynamic system that evolves with the application. Engineers must understand how rules are evaluated, how request and response data flows through the WAF, and how each action affects the final response. The most common failures—misleading rule priorities, header mismatches, and unvalidated transformations—arise not from complexity, but from subtle expectations. Mastery comes from anticipating how each rule behaves under real load, and from observing the WAF’s logs and metrics during incidents.

This page was rewritten on 17 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.