Octopus
1.x
Docs/Octopus/Middleware
Open

Reading6 min
Updated31 Jul 2026
Sourcev1/middleware/index.mdx

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.

Warning

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:

1

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)
Response

Compression 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.

Note

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-ID injection toward upstreams. The proxy's header transformer (octopus-proxy) adds an X-Request-ID header 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 separate RequestId middleware in octopus-middleware is 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 returns 429 with Retry-After when 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-level cors — see CORS: setting the top-level cors block adds the CORS middleware, and routes[].cors overrides 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.

MiddlewareSource fileStatusNotes
Auth gatewayauth_gateway.rsConfigurable (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.
CORScors.rsConfigurable (global + per-route)Added from the global cors config; applies routes[].cors overrides via MatchedRouteCors. See CORS.
Rate limitrate_limit.rsConfigurable (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 IDrequest_id.rsBuilder-onlyDistinct from the proxy's always-on X-Request-ID injection.
Timeouttimeout.rsBuilder-onlyPer-request timeout. Note gateway.request_timeout and routes[].timeout are applied by the proxy/router, not this middleware.
Logginglogging.rsBuilder-onlyRequest/response access logging.
Cachingcaching.rsBuilder-onlyIn-memory response cache with TTL + FIFO eviction.
Security headerssecurity_headers.rsConfigurable (gateway.security_headers)Adds HSTS, X-Frame-Options, CSP, etc. when enabled: true. See Security headers.
WAFwaf.rsBuilder-onlySQLi/XSS pattern detection, block/log modes.
IP filterip_filter.rsBuilder-onlyAllowlist/blocklist by IP/CIDR.
Forward authforward_auth.rsBuilder-onlyDelegates auth to an external subrequest. Also available as an auth provider — see Authentication.
JWTjwt.rsBuilder-onlyStandalone JWT validation. JWT auth is normally done via the auth provider, not this middleware.
Header transformheader_transform.rsBuilder-onlyAdd/set/remove/rename request & response headers.
Body transformbody_transform.rsBuilder-onlyField-level JSON body transforms (remove/rename/set/redact).
Redirectredirect.rsBuilder-onlyRegex path redirects, HTTPS enforcement, trailing-slash normalization.
Retryretry.rsBuilder-onlyExponential-backoff retries. The proxy has its own retry path; this is the middleware form.
Circuit breakercircuit_breaker.rsBuilder-onlyPer-upstream Closed→Open→Half-Open. See Circuit breaker concept.
Canarycanary.rsBuilder-onlyWeighted traffic splitting between upstreams.
Deduplicationdeduplication.rsBuilder-onlyRequest idempotency / dedup.
Bot detectionbot_detection.rsBuilder-onlyUser-Agent-based bot blocking.
Connection limitsconnection_limits.rsBuilder-onlyCaps concurrent connections.
Request limitsrequest_limits.rsBuilder-onlyCaps request body/header/URI size.
Audit loggeraudit_logger.rsBuilder-onlyStructured security/audit event logging.
Note

"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