Middleware
Middleware in Octopus is Rust code that wraps the request/response path. Each
middleware implements the octopus_core::middleware::Middleware trait and is
invoked through a Next chain, in registration order on the way in and reverse
order on the way out.
This page documents the real, source-verified model: which middleware the
gateway actually activates from configuration, what runs unconditionally, and a
complete catalogue of every implementation shipped in the octopus-middleware
crate — including the many that exist but are not wired to configuration
today.
The top-level Config struct has no middleware
list. You cannot declare an arbitrary, ordered middleware stack in YAML. The
global chain is assembled in code from typed config — currently compression,
CORS (when a top-level cors block is set), per-route rate limiting (when any
route sets rate_limit), security headers (when gateway.security_headers.enabled),
and the auth gateway. Older examples that show a middleware: [...] array are
inaccurate — that key is silently ignored.
The real middleware model01
When the server starts, it builds a single global chain
(crates/octopus-runtime/src/server.rs) and applies it to every HTTP request
in crates/octopus-runtime/src/handler.rs. The chain is built from exactly two
optional entries, in this order:
Compression (optional, global)
Added only when gateway.compression.enabled
is true (the default). This uses the octopus-compression crate's
CompressionMiddleware and is configured entirely by the gateway.compression
block. See Compression.
Auth gateway (optional, global)
Added only when there is at least one entry in
auth_providers or
auth.global_enforce is true. This is
octopus_middleware::AuthGatewayMiddleware, driven by the auth and
auth_providers config. See Authentication gateway.
If neither condition holds, the global chain is empty and requests go straight to routing and proxying.
Execution order
Request
→ [Compression] (if gateway.compression.enabled)
→ [Auth gateway] (if auth_providers non-empty OR auth.global_enforce)
→ Route match → Upstream proxy
← Auth gateway (pass-through on the way out)
← Compression (compresses the response body)
ResponseCompression is registered first, so it is the outermost wrapper: it sees the final response body and compresses it after the auth gateway and proxy have run. The auth gateway runs second, authenticating/authorizing before the request is proxied.
The handler pre-matches the route and injects auth/CORS context into request
extensions before running the chain, then the final handler does the actual
route match and proxy. So per-route auth settings (auth_provider,
skip_auth, require_roles, require_scopes, authz_rule) are honored by
the auth gateway. See Routes.
Always-on behavior (not via the middleware crate)02
One request-shaping behavior is unconditional but does not come from a
octopus-middleware middleware:
X-Request-IDinjection toward upstreams. The proxy's header transformer (octopus-proxy) adds anX-Request-IDheader to the forwarded request when one is absent. This is hardcoded on by default (ProxyConfig::default()) and is not exposed through gateway configuration. Note this is the proxy behavior — the separateRequestIdmiddleware inoctopus-middlewareis not part of the runtime chain.
There is no always-on logging, timeout, or request-ID middleware in the chain.
Request access logging is emitted via tracing from the runtime/proxy layers, not
from the logging middleware.
Per-route settings03
Routes accept rate_limit and cors blocks, and
there is a top-level cors block — all enforced today:
routes[].rate_limit— a route-aware limiter (RouteRateLimiter) enforces a fixed window per route (keyed by the route's path) and returns429withRetry-Afterwhen exceeded. It is added to the chain whenever any route declares a limit. The counter is currently in-process (per replica); a shared backend for cross-replica limits is planned.routes[].cors/ top-levelcors— see CORS: setting the top-levelcorsblock adds the CORS middleware, androutes[].corsoverrides it per route.
Catalogue of built-in middleware04
The octopus-middleware crate ships roughly two dozen middleware. The table
below lists every implementation and its current wiring status:
Configurable — activated from gateway configuration today.
Builder-only — fully implemented and reachable via
MiddlewareBuilder/direct construction for custom embeddings, but not wired to any gateway config key or the runtime chain.
| Middleware | Source file | Status | Notes |
| Auth gateway | auth_gateway.rs | Configurable (global) | Added when auth_providers non-empty or auth.global_enforce. See Authentication gateway. |
| Compression | (octopus-compression) | Configurable (global) | Added when gateway.compression.enabled. See Compression. The crate also has its own compression.rs, used only via the builder. |
| CORS | cors.rs | Configurable (global + per-route) | Added from the global cors config; applies routes[].cors overrides via MatchedRouteCors. See CORS. |
| Rate limit | rate_limit.rs | Configurable (per-route) | routes[].rate_limit is enforced by a route-aware limiter (RouteRateLimiter, in-process state backend). The token-bucket RateLimit is builder-only. See Rate limiting. |
| Request ID | request_id.rs | Builder-only | Distinct from the proxy's always-on X-Request-ID injection. |
| Timeout | timeout.rs | Builder-only | Per-request timeout. Note gateway.request_timeout and routes[].timeout are applied by the proxy/router, not this middleware. |
| Logging | logging.rs | Builder-only | Request/response access logging. |
| Caching | caching.rs | Builder-only | In-memory response cache with TTL + FIFO eviction. |
| Security headers | security_headers.rs | Configurable (gateway.security_headers) | Adds HSTS, X-Frame-Options, CSP, etc. when enabled: true. See Security headers. |
| WAF | waf.rs | Builder-only | SQLi/XSS pattern detection, block/log modes. |
| IP filter | ip_filter.rs | Builder-only | Allowlist/blocklist by IP/CIDR. |
| Forward auth | forward_auth.rs | Builder-only | Delegates auth to an external subrequest. Also available as an auth provider — see Authentication. |
| JWT | jwt.rs | Builder-only | Standalone JWT validation. JWT auth is normally done via the auth provider, not this middleware. |
| Header transform | header_transform.rs | Builder-only | Add/set/remove/rename request & response headers. |
| Body transform | body_transform.rs | Builder-only | Field-level JSON body transforms (remove/rename/set/redact). |
| Redirect | redirect.rs | Builder-only | Regex path redirects, HTTPS enforcement, trailing-slash normalization. |
| Retry | retry.rs | Builder-only | Exponential-backoff retries. The proxy has its own retry path; this is the middleware form. |
| Circuit breaker | circuit_breaker.rs | Builder-only | Per-upstream Closed→Open→Half-Open. See Circuit breaker concept. |
| Canary | canary.rs | Builder-only | Weighted traffic splitting between upstreams. |
| Deduplication | deduplication.rs | Builder-only | Request idempotency / dedup. |
| Bot detection | bot_detection.rs | Builder-only | User-Agent-based bot blocking. |
| Connection limits | connection_limits.rs | Builder-only | Caps concurrent connections. |
| Request limits | request_limits.rs | Builder-only | Caps request body/header/URI size. |
| Audit logger | audit_logger.rs | Builder-only | Structured security/audit event logging. |
"Builder-only" middleware are real, tested implementations — they are simply not exposed through gateway configuration in the current release. They can be composed into a chain programmatically (see below) or used as building blocks for embedding Octopus as a library.
Using middleware programmatically05
For embedded/library use, octopus_middleware::MiddlewareBuilder composes a
chain. The builder exposes helpers for a subset of the catalogue
(with_request_id, with_timeout, with_logging, with_rate_limit,
with_cors, with_compression, with_ip_filter, with_forward_auth,
with_caching) plus with_middleware for any Arc<dyn Middleware>:
use octopus_middleware::MiddlewareBuilder;
let chain = MiddlewareBuilder::new()
.with_request_id()
.with_logging()
.with_cors()
.with_compression()
.build(); // -> Arc<[Arc<dyn Middleware>]>This is the only way to activate the builder-only middleware today. The standard
gateway binary does not call the builder; it assembles its chain directly:
compression, CORS, security headers (when gateway.security_headers.enabled),
then the auth gateway.
Pages in this section06
Related07
Middleware concept — the architectural model.
Gateway configuration — compression, timeouts.
Routes — per-route auth,
rate_limit,cors.Authentication & authorization — providers and authz.
Security — the end-to-end security model.