Filters

Filters provide a flexible pipeline for request and response processing. They can be built-in (rate-limit, headers, CORS, compression) or external agents. Filters are defined centrally in the filters block with unique IDs, then referenced by name in route configurations.

Basic Configuration

filters {
    filter "api-rate-limit" {
        type "rate-limit"
        max-rps 100
        burst 20
        key "client-ip"
    }

    filter "add-security-headers" {
        type "headers"
        phase "response"
        set {
            "X-Content-Type-Options" "nosniff"
            "X-Frame-Options" "DENY"
        }
    }
}

routes {
    route "api" {
        matches {
            path-prefix "/api/"
        }
        upstream "backend"
        filters "api-rate-limit" "add-security-headers"
    }
}

Filter Types

Rate Limiting

Rate limiting using a token bucket algorithm. Supports local (single-instance) or distributed (Redis) backends.

filter "rate-limiter" {
    type "rate-limit"
    max-rps 100              // Maximum requests per second
    burst 20                 // Token bucket size
    key "client-ip"          // Rate limit key
    on-limit "reject"        // Action when exceeded
    status-code 429          // Response status
    limit-message "Too many requests"
}

Rate Limit Keys

KeyDescription
client-ipRate limit per client IP address (default)
pathRate limit per request path
routeGlobal rate limit for the route
client-ip-and-pathCombined client IP and path
header:X-API-KeyRate limit by specific header value

Rate Limit Actions

ActionDescription
rejectReject with 429 status (default)
delayQueue request until tokens available (up to max-delay-ms)
log-onlyLog but allow request through

Distributed Rate Limiting with Redis

filter "distributed-limiter" {
    type "rate-limit"
    max-rps 1000
    burst 100
    backend "redis" {
        url "redis://127.0.0.1:6379"
        key-prefix "sentinel:ratelimit:"
        pool-size 10
        timeout-ms 50
        fallback-local #true
    }
}

Global Rate Limits

Apply rate limits at the server level before route-specific limits:

rate-limits {
    default-rps 100        // Default for routes without explicit limits
    default-burst 20
    key "client-ip"

    global {
        max-rps 10000      // Server-wide limit
        burst 1000
        key "client-ip"
    }
}

Headers Filter

Manipulate request or response headers:

filter "security-headers" {
    type "headers"
    phase "response"       // "request", "response", or "both"

    // Set headers (overwrites existing)
    set {
        "X-Content-Type-Options" "nosniff"
        "X-Frame-Options" "DENY"
        "X-XSS-Protection" "1; mode=block"
        "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
    }

    // Add headers (preserves existing)
    add {
        "X-Request-ID" "${trace_id}"
    }

    // Remove headers
    remove "Server" "X-Powered-By"
}

CORS Filter

Handle Cross-Origin Resource Sharing:

filter "cors" {
    type "cors"
    allowed-origins "https://example.com" "https://app.example.com"
    allowed-methods "GET" "POST" "PUT" "DELETE" "OPTIONS"
    allowed-headers "Content-Type" "Authorization" "X-Request-ID"
    exposed-headers "X-Request-ID" "X-RateLimit-Remaining"
    allow-credentials #true
    max-age-secs 86400    // Preflight cache duration
}
OptionDefaultDescription
allowed-origins*Origins allowed to access (use * for any)
allowed-methodsCommon methodsHTTP methods allowed
allowed-headersNoneRequest headers client can send
exposed-headersNoneResponse headers client can read
allow-credentialsfalseAllow cookies/auth headers
max-age-secs86400Preflight cache duration

Compression Filter

Compress response bodies:

filter "compress" {
    type "compress"
    algorithms "brotli" "gzip" "zstd"
    min-size 1024          // Minimum size to compress (bytes)
    level 6                // Compression level (1-9)
    content-types "text/html" "text/css" "application/json" "application/javascript"
}
AlgorithmDescription
gzipWidely supported, good compression
brotliBetter compression, modern browsers
deflateLegacy, wide support
zstdFast compression/decompression

GeoIP Filter

Filter requests based on geographic location:

// Block mode - block specific countries
filter "block-countries" {
    type "geo"
    database-path "/etc/sentinel/GeoLite2-Country.mmdb"
    action "block"
    countries "RU" "CN" "KP" "IR"
    on-failure "closed"    // Block if lookup fails
    status-code 403
    block-message "Access denied from your region"
}

// Allow mode - allow only specific countries
filter "us-only" {
    type "geo"
    database-path "/etc/sentinel/GeoLite2-Country.mmdb"
    action "allow"
    countries "US" "CA"
    status-code 451        // Unavailable for legal reasons
}

// Log-only mode - tag requests with country
filter "geo-tagging" {
    type "geo"
    database-path "/etc/sentinel/GeoLite2-Country.mmdb"
    action "log-only"
    add-country-header #true
}

Geo Filter Options

OptionDefaultDescription
database-pathRequiredPath to GeoIP database (.mmdb or .bin)
database-typeAuto-detectmaxmind or ip2location
actionblockblock, allow, or log-only
countriesEmptyISO 3166-1 alpha-2 codes (e.g., US, CN)
on-failureopenopen (allow) or closed (block) on lookup failure
status-code403HTTP status for blocked requests
block-messageNoneCustom message for blocked requests
add-country-headertrueAdd X-Country-Code header
cache-ttl-secs3600Cache TTL for lookups

Timeout Filter

Override timeouts for specific routes:

filter "long-timeout" {
    type "timeout"
    request-timeout-secs 300       // Total request timeout
    upstream-timeout-secs 120      // Backend timeout
    connect-timeout-secs 30        // Connection timeout
}

Log Filter

Add detailed logging for specific routes:

filter "debug-logging" {
    type "log"
    log-request #true
    log-response #true
    log-body #true
    max-body-log-size 4096
    level "debug"
    fields "user-agent" "content-type" "x-request-id"
}

Agent Filter

Delegate processing to an external agent:

filter "waf" {
    type "agent"
    agent "waf-agent"      // Reference to agent defined in agents block
    phase "request"        // "request", "response", or "both"
    timeout-ms 200
    failure-mode "open"    // "open" or "closed"
}

Filter Phases

Filters execute at different phases of the request lifecycle:

PhaseDescription
requestBefore forwarding to upstream
responseAfter receiving from upstream
bothBoth request and response phases

Filter phase mapping:

Filter TypeDefault Phase
rate-limitRequest
headersConfigurable
corsBoth
compressResponse
geoRequest
timeoutRequest
logConfigurable
agentConfigurable

Filter Ordering

Filters execute in the order specified in the route:

route "api" {
    filters "rate-limit" "auth" "cors" "logging"
    //       ^^^^^^^^^    ^^^^   ^^^^   ^^^^^^^
    //       1st          2nd    3rd    4th
}

For request phase:

  1. Rate limit check
  2. Auth validation
  3. CORS headers (preflight)
  4. Logging

For response phase (reverse order of declaration):

  1. Logging
  2. CORS headers
  3. (Auth typically doesn’t run on response)
  4. (Rate limit doesn’t run on response)

Complete Example

filters {
    // Rate limiting with Redis backend
    filter "api-limiter" {
        type "rate-limit"
        max-rps 100
        burst 20
        key "header:X-API-Key"
        on-limit "reject"
        backend "redis" {
            url "redis://redis:6379"
            fallback-local #true
        }
    }

    // Geo-blocking
    filter "geo-block" {
        type "geo"
        database-path "/etc/sentinel/GeoLite2-Country.mmdb"
        action "block"
        countries "RU" "CN"
        on-failure "open"
    }

    // CORS for API
    filter "api-cors" {
        type "cors"
        allowed-origins "https://app.example.com"
        allowed-methods "GET" "POST" "PUT" "DELETE"
        allowed-headers "Content-Type" "Authorization"
        allow-credentials #true
    }

    // Security headers
    filter "security" {
        type "headers"
        phase "response"
        set {
            "X-Content-Type-Options" "nosniff"
            "X-Frame-Options" "DENY"
        }
        remove "Server"
    }

    // Response compression
    filter "compress" {
        type "compress"
        algorithms "brotli" "gzip"
        min-size 1024
    }

    // WAF agent
    filter "waf" {
        type "agent"
        agent "waf-agent"
        timeout-ms 100
        failure-mode "closed"
    }
}

routes {
    route "public-api" {
        matches {
            path-prefix "/api/v1/"
        }
        upstream "api-backend"
        filters "geo-block" "api-limiter" "api-cors" "security" "compress"
    }

    route "secure-api" {
        matches {
            path-prefix "/api/admin/"
        }
        upstream "admin-backend"
        filters "geo-block" "waf" "api-limiter" "security"
    }
}

Default Values

FilterSettingDefault
Rate Limitburst10
Rate Limitkeyclient-ip
Rate Limiton-limitreject
Rate Limitstatus-code429
Rate Limitmax-delay-ms5000
Headersphaserequest
Compressalgorithmsgzip, brotli
Compressmin-size1024
Compresslevel6
CORSallowed-origins*
CORSmax-age-secs86400
Geoactionblock
Geoon-failureopen
Geostatus-code403
Loglevelinfo
Logmax-body-log-size4096

Next Steps