Middleware ​
Middleware functions execute before your route handlers. They can modify the request, response, or halt execution (e.g., for authentication).
Using Middleware ​
To add global middleware to the server, use server.use(). Middleware functions follow the signature fn (*httpx.router.Context, httpx.router.NextFn) anyerror!httpx.router.Response.
// Add standard CORS middleware
try server.use(httpx.middleware.cors);
// Add security headers (Helmet)
try server.use(httpx.middleware.helmet);
// Add error recovery (500 fallback)
try server.use(httpx.middleware.recovery);Built-in Middleware ​
httpx.zig includes built-in middleware under the httpx.middleware namespace:
httpx.middleware.cors: Handles preflightOPTIONSrequests (204 No Content) and injects CORS response headers.httpx.middleware.helmet: Defensive headers (X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Content-Security-Policy).httpx.middleware.recovery: Intercepts uncaught handler errors and returns safe HTTP 500 responses without crashing the connection loop.httpx.middleware.logging: Pass-through compatibility hook; use the serverlogging.callbackfor structured request events.httpx.middleware.csrf: Double-submit cookie CSRF protection. Safe methods receive a token cookie when absent; state-changing requests must echo the token in theX-CSRF-Tokenheader or receive403 Forbidden.httpx.RateLimiter: Multi-dimensional token bucket rate limiting (by IP, Bearer token, route, or custom key).httpx.middleware.generateCsrfToken/verifyCsrfToken: Low-level helpers.generateCsrfTokenis test-only; production code should use the middleware, which draws tokens from OS entropy.
Writing Custom Middleware ​
A custom middleware is a function that receives the *httpx.router.Context and the NextFn callback:
fn banCheckMiddleware(ctx: *httpx.router.Context, next: httpx.router.NextFn) anyerror!httpx.router.Response {
// 1. Pre-processing
if (ctx.header("X-Ban")) |_| {
return ctx.textStatus(403, "Forbidden");
}
// 2. Call next in chain
var response = try next(ctx);
// 3. Post-processing (optional)
// Modify response or inspect response.status
return response;
}
try server.use(banCheckMiddleware);Scopes and Ordering ​
Middleware runs in a deterministic order: router-wide (server.use) first, then route-level (.{ .middleware = ... } on registration), then the handler. Groups prepend their middleware to each route:
const api = server.router.group("/api", .{ .middleware = &.{authMw} });
try api.get("/users", listUsers, .{ .middleware = &.{auditMw} });
// order: server.use middlewares → authMw → auditMw → listUsersCompression ​
The server compresses handler responses automatically for HTTP/1.1, HTTP/2, and HTTP/3. It parses Accept-Encoding, selects zstd, br, gzip, or deflate, and sends Content-Encoding plus Vary: Accept-Encoding only when the coded body is smaller. Negotiation is driven by .compression in the options passed to httpx.Server.init; bodies larger than .maxCompressionInput (128 KiB by default) are sent unchanged. Unsupported or forbidden encodings produce 406 Not Acceptable.
The httpx.compression codec remains available when a handler needs explicit control over compression.
Rate Limiting ​
Use httpx.RateLimiter for per-key request-rate enforcement (see src/web/middleware/rateLimiter.zig for RateLimitPolicy, RateLimitResult, and RateLimitDimension).
CSRF Protection ​
Register the built-in middleware globally:
try server.use(httpx.middleware.csrf);Safe methods (GET, HEAD, OPTIONS, TRACE) pass through and receive a csrf_token cookie when one is not already present. POST, PUT, PATCH, DELETE, and other state-changing methods compare the cookie with the X-CSRF-Token header using a constant-time check, returning 403 Forbidden when either value is missing or they differ.
Use httpx.middleware.verifyCsrfToken for a constant-time comparison in custom policies. Avoid generateCsrfToken in production: it accepts a caller-supplied PRNG and exists for tests. The built-in middleware defines safe cookie name, header name, path, SameSite, and Secure defaults configured via .{}.
