Skip to content

HTTP Middleware

Middleware processes HTTP requests before and after route handling.

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 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"

Pre-match

Cross-Origin Resource Sharing for browser requests.

middleware:
- cors
options:
cors.allow.origins: "https://app.example.com"
cors.allow.credentials: "true"
OptionDefaultDescription
cors.allow.origins*Allowed origins (comma-separated, supports *.example.com)
cors.allow.methodsGET,POST,PUT,DELETE,OPTIONS,PATCHAllowed methods
cors.allow.headersOrigin,Content-Type,Accept,Authorization,X-Requested-WithAllowed request headers
cors.expose.headers-Headers exposed to client
cors.allow.credentialsfalseAllow cookies/auth
cors.max.age86400Preflight cache (seconds)
cors.allow.private.networkfalsePrivate network access

OPTIONS preflight requests are handled automatically.


Pre-match

Token bucket rate limiting with per-key tracking.

middleware:
- ratelimit
options:
ratelimit.requests: "100"
ratelimit.window: "1m"
ratelimit.key: "ip"
OptionDefaultDescription
ratelimit.requests100Requests per window
ratelimit.window1mTime window
ratelimit.burst20Burst capacity
ratelimit.keyipKey strategy
ratelimit.cleanup_interval5mCleanup frequency
ratelimit.entry_ttl10mEntry expiration
ratelimit.max_entries100000Max tracked keys

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

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


Pre-match

Gzip compression for responses.

middleware:
- compress
options:
compress.level: "default"
compress.min.length: "1024"
OptionDefaultDescription
compress.leveldefaultfastest, default, or best
compress.min.length1024Minimum response size (bytes)

Only compresses when client sends Accept-Encoding: gzip.


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"
OptionDefaultDescription
real_ip.trusted.subnetsPrivate networksTrusted proxy CIDRs
real_ip.trust_allfalseTrust all sources (insecure)

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


Pre-match

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

middleware:
- token_auth
options:
token_auth.store: "app:tokens"
OptionDefaultDescription
token_auth.storerequiredToken store registry ID
token_auth.header.nameAuthorizationHeader name
token_auth.header.prefixBearer Header prefix
token_auth.query.paramx-auth-tokenQuery parameter fallback
token_auth.cookie.namex-auth-tokenCookie fallback

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


Pre-match

Prometheus-style HTTP metrics. No configuration options.

middleware:
- metrics
MetricTypeDescription
wippy_http_requests_totalCounterTotal requests
wippy_http_request_duration_secondsHistogramRequest latency
wippy_http_requests_in_flightGaugeConcurrent requests

Post-match

Authorization based on matched endpoint. Requires actor from token_auth.

post_middleware:
- endpoint_firewall
post_options:
endpoint_firewall.action: "access"
OptionDefaultDescription
endpoint_firewall.actionaccessPermission action to check

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


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"
OptionDefaultDescription
resource_firewall.actionaccessPermission action
resource_firewall.targetrequiredResource registry ID

Pre-match

Serve files via X-Sendfile header from handlers.

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

Handler sets headers to trigger file serving:

HeaderDescription
X-SendfileFile path within filesystem
X-File-NameDownload filename

Supports range requests for resumable downloads.


Post-match

Relay WebSocket connections to processes. See WebSocket Relay.

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

Post-match

Stream Server-Sent Events from processes. See Server-Sent Events.

post_middleware:
- sse_relay
post_options:
sserelay.allowed.origins: "https://app.example.com"

Pre-match

Records OpenTelemetry spans and metrics for incoming requests. Registered automatically when OTel is enabled; acts as a no-op otherwise.

middleware:
- otel

Takes no options. Works alongside the metrics middleware; enable both when you need Prometheus counters and OTel traces.


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