Middleware Overview#
Webrick’s middleware pipeline runs before and after your route handler. Use pre-global middleware for request hardening and shaping; use post-global middleware for response transformation and delivery. You can also attach per-route middleware via route options or attributes.
The pipeline#
Client → [ Pre-Global Middleware ] → Route Handler → [ Post-Global Middleware ] → Emitter → Client
Pre-global (typical)#
Gateway Hardening – basic security headers & request sanity checks
Telemetry – request IDs, timing, tracing hooks
Maintenance Mode – short-circuit with 503 during maintenance windows
Request Limits – size/time guards before reaching handlers
Throttle – per-key rate limiting, emits 429 + retry headers
Cookie Encryption – decrypts incoming cookies into
RequestNormalize Method – honors
_methodoverride for HTML formsInput Sanitizer – trims/normalizes common inputs
Negotiation – parses
Accept/language, sets attributesResponse Cache – fast path for cacheable GETs (if configured)
Cache Validators – handles
ETag/Last-Modified, returns 304/412 when applicable
Post-global (typical)#
Compression – negotiates
zstd/br/gzipsafely; coordinates ETagsCORS & Policies – CORS preflight/allow headers + security policies
Vary Accumulator – canonicalizes and appends
VarytokensResponse Linter (dev) – catches header/body anti-patterns early
Keep order intentional: validators before handlers; compression after handlers.
Wiring middleware#
You attach global middleware when booting the kernel:
$preGlobal = [
\Infocyph\Webrick\Middleware\GatewayHardeningMiddleware::class,
\Infocyph\Webrick\Middleware\TelemetryMiddleware::class,
\Infocyph\Webrick\Middleware\MaintenanceModeMiddleware::class,
\Infocyph\Webrick\Middleware\RequestLimitsMiddleware::class,
\Infocyph\Webrick\Middleware\ThrottleMiddleware::class,
new \Infocyph\Webrick\Middleware\CookieEncryptionMiddleware($_ENV['WEBRICK_COOKIE_KEY'] ?? 'dev'),
\Infocyph\Webrick\Middleware\NormalizeMethodMiddleware::class,
\Infocyph\Webrick\Middleware\InputSanitizerMiddleware::class,
\Infocyph\Webrick\Middleware\NegotiationMiddleware::class,
\Infocyph\Webrick\Middleware\ResponseCacheMiddleware::class,
\Infocyph\Webrick\Middleware\CacheValidatorsMiddleware::class,
];
$postGlobal = [
\Infocyph\Webrick\Middleware\CompressionMiddleware::class,
\Infocyph\Webrick\Middleware\CorsAndPoliciesMiddleware::class,
\Infocyph\Webrick\Middleware\VaryAccumulatorMiddleware::class,
\Infocyph\Webrick\Middleware\ResponseLinterMiddleware::class, // dev only
];
Per-route middleware:
Route::get('/secure/{id:int}', fn()=>['ok'=>true], [
'middleware' => ['verifySignedUrl','throttle:5,60'],
]);
Attributes also accept middleware: arrays.
Choosing the right order#
Hardening & limits first → drop bad or oversized requests early
Throttling before any heavy work
Normalization & sanitization before reading input
Negotiation before handlers (so
Response::auto()has context)Response cache / validators before handler to short-circuit
Compression / CORS / Vary after handler, final shaping
Performance notes#
Middleware classes should be stateless or light; reuse shared services via DI.
Prefer header appends over string manipulation of big bodies in post-globals.
Use route cache warmups so matcher/registrar work is done ahead of time.
Avoid expensive I/O in pre-globals unless it short-circuits a lot of work.
Testing strategy#
Unit test individual middleware (given a request → expect headers/status).
Integration test the ordered stack to catch interactions (e.g., ETag + compression).
For throttling and response cache, add time-based tests with controlled clocks.
What’s next#
Deep-dives for each middleware:
compression.md– encodings, ETag strategies, caveatscache-validators.md– 304/412 logic, ETag/Last-Modified providersvary-accumulator.md– consistentVarymanagementcors-and-policies.md– cross-origin and security headersthrottle.md– limits, keys, headersrequest-limits.md– body size/time capscookie-encryption.md– encrypt/decrypt lifecyclemaintenance-mode.md– toggles & messagestelemetry.md– IDs, timings, tracing hooksnormalize-method.md–_methodoverride rulesinput-sanitizer.md– best-effort normalizationnegotiation.md– content & locale selectionresponse-cache.md– when to serve from cacheresponse-linter.md– dev-only quality guard