Middleware

ZealPHP uses PSR-15 middleware. Add with $app->addMiddleware(). The last added runs outermost (first to process request, last to process response).

Built-in middleware

ClassConstructorWhat it does
CorsMiddleware($origins, $methods, $headers, $credentials, $maxAge)CORS preflight + Access-Control headers on every response
ETagMiddleware(none)Generates W/"md5" ETag, returns 304 on cache hit
CompressionMiddleware($minLength=1024, $level=6, $skipProxiedRequests=false)Reference gzip/deflate middleware; runtime compression is handled by OpenSwoole by default
app.php — middleware registration order
$app->addMiddleware(new CorsMiddleware());         // outermost — handles preflight
$app->addMiddleware(new ETagMiddleware());         // generates ETag
$app->addMiddleware(new AuthMiddleware());         // your custom middleware
// ResponseMiddleware is always innermost (built-in)

Live demos

GET CORS — Access-Control-Allow-Origin on every response
// Add middleware once in app.php:
$app->addMiddleware(new CorsMiddleware(['*']));

// Hit any endpoint with Origin header:
// curl -H "Origin: http://app.test" https://php.zeal.ninja/demo/middleware/cors
// → Access-Control-Allow-Origin: *
LIVE OUTPUT Click Run →
GET ETag / 304 — conditional GET
// ETagMiddleware auto-generates W/"md5(body)" on GET
// Second request with If-None-Match: <etag> → 304 Not Modified

// First hit:
// curl -D - https://php.zeal.ninja/http/etag-test
// → ETag: W/"abc..."
// Second hit:
// curl -H 'If-None-Match: W/"abc..."' https://php.zeal.ninja/http/etag-test
// → HTTP/1.1 304 Not Modified (empty body)
LIVE OUTPUT Click Run →
GET Compression — gzip when Accept-Encoding: gzip
// OpenSwoole handles runtime compression by default.
// Keep CompressionMiddleware only as a reference if you disable http_compression.
// curl --compressed https://php.zeal.ninja/http/compress-test
// → Content-Encoding: gzip  (body is compressed)
LIVE OUTPUT Click Run →

Custom middleware

use Psr\Http\Message\{ResponseInterface, ServerRequestInterface};
use Psr\Http\Server\{MiddlewareInterface, RequestHandlerInterface};

class TimingMiddleware implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $start    = microtime(true);
        $response = $handler->handle($request);       // call inner stack
        $elapsed  = round((microtime(true) - $start) * 1000, 2);
        response_add_header('X-Response-Time', "$elapsed ms");
        return $response;
    }
}

// Register:
$app->addMiddleware(new TimingMiddleware());