# Middleware _Path: en/http/middleware_ ## Table of Contents - HTTP Middleware ## Content # 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: ```yaml 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. ```yaml middleware: # Pre-match - cors - compress options: cors.allow.origins: "*" post_middleware: # Post-match - endpoint_firewall post_options: endpoint_firewall.action: "access" ``` --- ### CORS {#cors} Pre-match Cross-Origin Resource Sharing for browser requests. ```yaml 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. ```yaml 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. ```yaml 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. ```yaml 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](system/security.md) for token store configuration. ```yaml 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. ```yaml 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`. ```yaml 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. ```yaml 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. ```yaml 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](http/websocket-relay.md). ```yaml post_middleware: - websocket_relay post_options: wsrelay.allowed.origins: "https://app.example.com" ``` --- ### SSE Relay {#sse_relay} Post-match Stream Server-Sent Events from processes. See [Server-Sent Events](http/sse.md). ```yaml post_middleware: - sse_relay post_options: sserelay.allowed.origins: "https://app.example.com" ``` --- ## Middleware Order Middleware executes in listed order. Recommended sequence: ```yaml 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 - [Routing](http/router.md) - Router configuration - [Security](system/security.md) - Token stores and policies - [WebSocket Relay](http/websocket-relay.md) - WebSocket handling - [Server-Sent Events](http/sse.md) - SSE streaming - [Terminal](system/terminal.md) - Terminal service ## Navigation Previous: Endpoint (http/endpoint) Next: Static Files (http/static)