HTTP Middleware

Middleware processes HTTP requests before and after route handling.

How Middleware Works

Middleware wraps HTTP handlers to add processing logic. Each middleware receives an options map and returns a handler wrapper:

middleware:
  - cors
  - ratelimit
options:
  cors.allow.origins: "https://example.com"
  ratelimit.requests: "100"

Options use dot notation: middleware_name.option.name. Legacy underscore format is supported for backward compatibility.

Pre-Match vs Post-Match

Pre-match runs before route matching—for cross-cutting concerns like CORS and compression. Post-match runs after the route is matched—for authorization that needs route info.
middleware:        # Pre-match
  - cors
  - compress
options:
  cors.allow.origins: "*"

post_middleware:   # Post-match
  - endpoint_firewall
post_options:
  endpoint_firewall.action: "access"

Available Middleware

CORS {#cors}

Pre-match

Cross-Origin Resource Sharing for browser requests.

middleware:
  - cors
options:
  cors.allow.origins: "https://app.example.com"
  cors.allow.credentials: "true"
Option Default Description
cors.allow.origins * Allowed origins (comma-separated, supports *.example.com)
cors.allow.methods GET,POST,PUT,DELETE,OPTIONS,PATCH Allowed methods
cors.allow.headers Origin,Content-Type,Accept,Authorization,X-Requested-With Allowed request headers
cors.expose.headers - Headers exposed to client
cors.allow.credentials false Allow cookies/auth
cors.max.age 86400 Preflight cache (seconds)
cors.allow.private.network false Private network access

OPTIONS preflight requests are handled automatically.


Rate Limiting {#ratelimit}

Pre-match

Token bucket rate limiting with per-key tracking.

middleware:
  - ratelimit
options:
  ratelimit.requests: "100"
  ratelimit.window: "1m"
  ratelimit.key: "ip"
Option Default Description
ratelimit.requests 100 Requests per window
ratelimit.window 1m Time window
ratelimit.burst 20 Burst capacity
ratelimit.key ip Key strategy
ratelimit.cleanup_interval 5m Cleanup frequency
ratelimit.entry_ttl 10m Entry expiration
ratelimit.max_entries 100000 Max tracked keys

Key strategies: ip, header:X-API-Key, query:api_key

Returns 429 Too Many Requests with headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.


Compression {#compress}

Pre-match

Gzip compression for responses.

middleware:
  - compress
options:
  compress.level: "default"
  compress.min.length: "1024"
Option Default Description
compress.level default fastest, default, or best
compress.min.length 1024 Minimum response size (bytes)

Only compresses when client sends Accept-Encoding: gzip.


Real IP {#real_ip}

Pre-match

Extract client IP from proxy headers.

middleware:
  - real_ip
options:
  real_ip.trusted.subnets: "10.0.0.0/8,172.16.0.0/12"
Option Default Description
real_ip.trusted.subnets Private networks Trusted proxy CIDRs
real_ip.trust_all false Trust all sources (insecure)

Header priority: True-Client-IP > X-Real-IP > X-Forwarded-For


Token Auth {#token_auth}

Pre-match

Token-based authentication. See Security for token store configuration.

middleware:
  - token_auth
options:
  token_auth.store: "app:tokens"
Option Default Description
token_auth.store required Token store registry ID
token_auth.header.name Authorization Header name
token_auth.header.prefix Bearer Header prefix
token_auth.query.param x-auth-token Query parameter fallback
token_auth.cookie.name x-auth-token Cookie fallback

Sets actor and security scope in context for downstream middleware. Does not block requests—authorization happens in firewall middleware.


Metrics {#metrics}

Pre-match

Prometheus-style HTTP metrics. No configuration options.

middleware:
  - metrics
Metric Type Description
wippy_http_requests_total Counter Total requests
wippy_http_request_duration_seconds Histogram Request latency
wippy_http_requests_in_flight Gauge Concurrent requests

Endpoint Firewall {#endpoint_firewall}

Post-match

Authorization based on matched endpoint. Requires actor from token_auth.

post_middleware:
  - endpoint_firewall
post_options:
  endpoint_firewall.action: "access"
Option Default Description
endpoint_firewall.action access Permission action to check

Returns 401 Unauthorized (no actor) or 403 Forbidden (permission denied).


Resource Firewall {#resource_firewall}

Post-match

Protect specific resources by ID. Useful at router level.

post_middleware:
  - resource_firewall
post_options:
  resource_firewall.action: "admin"
  resource_firewall.target: "app:admin-panel"
Option Default Description
resource_firewall.action access Permission action
resource_firewall.target required Resource registry ID

Sendfile {#sendfile}

Pre-match

Serve files via X-Sendfile header from handlers.

middleware:
  - sendfile
options:
  sendfile.fs: "app:downloads"

Handler sets headers to trigger file serving:

Header Description
X-Sendfile File path within filesystem
X-File-Name Download filename

Supports range requests for resumable downloads.


WebSocket Relay {#websocket_relay}

Post-match

Relay WebSocket connections to processes. See WebSocket Relay.

post_middleware:
  - websocket_relay
post_options:
  wsrelay.allowed.origins: "https://app.example.com"

Middleware Order

Middleware executes in listed order. Recommended sequence:

middleware:
  - real_ip       # 1. Extract real IP first
  - cors          # 2. Handle CORS preflight
  - compress      # 3. Set up response compression
  - ratelimit     # 4. Check rate limits
  - metrics       # 5. Record metrics
  - token_auth    # 6. Authenticate requests

post_middleware:
  - endpoint_firewall  # Authorize after route match

See Also