Middleware
Middleware processes requests before they reach upstreams and responses before they return to clients.
Overview01
Middleware in Octopus is Rust code that wraps the request/response path. Each middleware runs in registration order on the way in and reverse order on the way out:
Request → M1 → M2 → M3 → Upstream → M3 → M2 → M1 → ResponseThere is no middleware: configuration array. The global chain is
assembled in code (crates/octopus-runtime/src/server.rs) and contains at most
two entries: compression (when gateway.compression.enabled, the default)
and the auth gateway (when auth providers are configured or
auth.global_enforce is on). Compression is registered first, so it is the
outermost wrapper and compresses the response on the way out. The
octopus-middleware crate ships many more middleware (CORS, rate limiting,
caching, security headers, WAF, …), but they are builder-only — not wired
to gateway config. Per-route rate_limit and cors are parsed and stored but
not enforced in the live chain. See Middleware reference for
the full catalogue and wiring status.
How Middleware Works02
Execution Order
The chain is built in code, not from config. The live order is:
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)
ResponseEach middleware is configured through its own config block (gateway.compression
and auth / auth_providers), not through a middleware: list.
Short-Circuiting
Middleware can stop the chain early — for example, the auth gateway returns
401/403 without proxying:
Request → Auth gateway (401/403) ──┐
↓
ResponseBuilt-in Middleware03
Of the middleware below, only authentication (via the auth gateway and
auth_providers) and compression (via gateway.compression) run in the
live chain today. Rate limiting and CORS are implemented but
builder-only — their config is parsed but not enforced at request time. The
YAML examples here are illustrative; see the linked reference pages for the
exact, source-verified schema.
Authentication
middleware:
- auth
auth:
type: jwt
secret: ${JWT_SECRET}
algorithm: HS256
header: Authorization
scheme: Bearer
excluded_paths:
- "/api/auth/login"
- "/_/health"See Authentication Middleware for details.
Rate Limiting
middleware:
- rate_limit
rate_limit:
strategy: token-bucket
requests_per_second: 100
burst: 200
key_by: ip # or: user_id, api_key, headerSee Rate Limiting Middleware for details.
CORS
middleware:
- cors
cors:
allowed_origins: ["https://example.com"]
allowed_methods: [GET, POST, PUT, DELETE]
allowed_headers: ["Content-Type", "Authorization"]
exposed_headers: ["X-Request-ID"]
allow_credentials: true
max_age: 3600See CORS Middleware for details.
Compression
middleware:
- compression
compression:
enabled: true
level: 6
min_size: 1024
types:
- "application/json"
- "text/html"See Compression Middleware for details.
Custom Middleware04
Create custom middleware via plugins:
use octopus_middleware::prelude::*;
pub struct CustomMiddleware;
#[async_trait]
impl Middleware for CustomMiddleware {
async fn call(
&self,
mut req: Request<Body>,
next: Next,
) -> Result<Response<Body>> {
// Modify request
req.headers_mut()
.insert("X-Custom-Header", "value".parse()?);
// Continue to next middleware
let mut resp = next.run(req).await?;
// Modify response
resp.headers_mut()
.insert("X-Processed-By", "octopus".parse()?);
Ok(resp)
}
}See Writing a Plugin for details.
Request Context05
Middleware can access and modify request context:
// Set context value
req.extensions_mut().insert(UserId("123".to_string()));
// Read context value
let user_id = req.extensions().get::<UserId>();Next Steps06
Learn about specific middleware:
Middleware reference - The real chain and full catalogue
Catalogue - Transformation, caching, and the rest