Dromos routes a request in 0.83 µs, with 20 routes or 2,000. It's the fastest of the eight PHP routers we benchmarked at 2,000 routes, it loads 5 files, and it speaks PSR-7, PSR-15 and PSR-17 when you need them.
The same routes in every router, timed on the same machine. Each lane is the time to route one request to a route with a parameter; shorter is faster. Pick a race:
1Dromos lean0.83 µs1.2M/s
2Symfony Routing1.36 µs735k/s
3Dromos full2.43 µs412k/s
4FastRoute16.7 µs60k/s
5Slim29.1 µs34k/s
6Laravel41.1 µs24k/s
7Mezzio141 µs7.1k/s
8Flight1,373 µs728/s
At 2,000 routes Dromos lean is the fastest of all eight. FastRoute is 27 times slower here than with 20 routes.
Log scale. µs per request, median of 5 runs. "/s" is requests routed a second on one core, routing work only.
PHP router benchmark: µs per request to a route with a parameter (lower is better)
Router
Worker, 20 routes
Worker, 2,000 routes
PHP-FPM cached, 20
PHP-FPM cached, 2,000
Files loaded
Dromos lean
0.81
0.83
1.73
1.74
5
Dromos
2.40
2.43
4.46
4.45
25
Symfony Routing
0.87
1.36
1.66
2.16
10
FastRoute
0.61
16.7
2.21
20.5
10
Laravel
39.9
41.1
63.8
66.3
75
Slim
11.1
29.1
47.1
1,653
68
Mezzio
11.5
141
72.6
2,925
103
Flight
25.8
1,373
74.4*
4,889*
10
* Flight has no route cache. FastRoute and Symfony Routing only match routes; Dromos lean does the same, then calls the controller. Full Dromos also builds PSR-7 requests and responses and runs middleware.
How it's measured
Routes: 5, 50 or 500 resources with four controller routes each (GET /res{n}, GET /res{n}/{id}, GET /res{n}/{id}/items/{item}, POST /res{n}), so 20, 200 or 2,000 routes. Each test requests a route of the last resource registered, the worst case for routers that try routes in turn.
Correctness: before anything is timed, every router must return the expected status and JSON body for every request, or the run stops.
Setup: each router as its documentation recommends, with its own route cache where it has one. Laravel is illuminate/routing on its own, without the rest of the framework.
Worker: the router is booted once, then requests are timed one after another, as under OpenSwoole, RoadRunner or FrankenPHP.
PHP-FPM cached: each iteration creates a new router, loads its routes from its cache and handles one request, in one process with opcache on.
Timing: 5 rounds of 60 ms per test with hrtime(), each script run 5 times in a fresh process; the figure is the median.
Machine: Apple M3 Pro, macOS, PHP 8.4.4 with opcache, no JIT, no Xdebug. Run it yourself from benchmark/compare; the full report has every test, real HTTP requests and every sample.
Why Dromos
A lightweight PHP router that stays fast as your API grows
Flat at any route count
Most PHP routers slow down as you add routes. FastRoute tries its routes in batches of about ten, so at 2,000 routes it's 27 times slower than at 20. Dromos finds exact paths with a hash lookup and groups routes with placeholders by their static prefix, so /users/42 is only tested against the routes under /users.
20 routes0.81
2000.82
2,0000.83
Lean when you want it, PSR when you need it
Router::match() only finds the route and leaves the rest to you, which is all many APIs need. Router::handle() gives you PSR-7 requests and responses and runs Dromos or PSR-15 middleware. Both share one route table and one cache, so you can start lean and add middleware later.
A route cache that stays correct
Under PHP-FPM, Router::loadRoutes() keeps your compiled routes in a PHP file that opcache holds in memory, so a request touches no file. It rebuilds itself when your routes change, at the moment PHP notices changed code, so it never serves stale routes.
Small enough to read in an afternoon
Lean mode is 5 files and 834 lines in 368 KB of memory. Full mode is 25 files, a third of them PSR-7 interfaces. Laravel's router loads 75 files and 26,248 lines. The only dependencies are the four PSR interface packages.
Quick start
Build a fast PHP API in three files
Install it with Composer. Dromos needs PHP 8.2 or later.
Register your routes in routes.php. Give controllers as [Class::class, 'method'] so they can be cached.
Load them in your front controller, then either match and call the handler yourself, or let Dromos handle the request with middleware.
Route parameters arrive as method arguments by name. A placeholder matches one path segment, and routes match in the order you register them.
use Dromos\Router;
Router::Get('/users', [UserController::class, 'index']);
Router::Get('/users/{id}', [UserController::class, 'show']);
Router::Post('/users', [UserController::class, 'store']);
Router::group('/admin', function ($group) {
$group->middleware(RequireAdmin::class);
$group->get('/stats', [StatsController::class, 'index']);
});
// public/index.php: PSR-7 request and response, with middlewareuse Dromos\Router;
use Dromos\Http\Request;
use Dromos\Http\Emitter\Emitter;
use Dromos\Middleware\CorsMiddleware;
Router::loadRoutes(__DIR__ . '/../routes.php', __DIR__ . '/../storage/cache/routes.php');
$router = new Router();
$router->addMiddleware(new CorsMiddleware());
(new Emitter())->emit($router->handle(new Request()));
// UserController::show(Response $response, string $id)// return $response->json(['id' => $id]);
Features
Everything a PHP API router needs, nothing it doesn't
Routing
Route parameters, groups with shared middleware, resource routes, and a 405 with an Allow header when only the method is wrong.
Lean mode
Router::match() returns [200, $handler, $params, $middleware], [404] or [405, $allowed], the shape FastRoute uses.
Route caching
A cached routes file for controller routes and a compiled table for closures. Both rebuild themselves.
PSR-7, PSR-15, PSR-17
Messages pass the PSR-7 integration tests, PSR-15 middleware runs as is, and HttpFactory passes the PSR-17 factory tests.
Middleware included
CORS, authentication and rate limiting, globally, per group or per route.
JSON first
JSON body parsing, JSON error responses, response helpers and input validation with rules like required|email.
Your container
Build controllers from any dependency injection container through ControllerResolverInterface.
PHP-FPM and workers
Opcache preloading for PHP-FPM, an emitter for OpenSwoole, and EmitterInterface for any other runtime.
FAQ
PHP router questions, answered with numbers
What is the fastest PHP router?
Of the eight PHP routers in our benchmark (Dromos, Dromos lean mode, FastRoute, Symfony Routing, Laravel, Slim, Mezzio and Flight), Dromos in lean mode is the fastest in every in-process test at 2,000 routes: 0.83 µs for a request to a route with a parameter, against 1.36 µs for Symfony Routing and 16.7 µs for FastRoute. With only 20 routes, FastRoute and Symfony win some tests by a fraction of a microsecond. Routers written in C, such as Phalcon's, weren't measured.
Is Dromos faster than FastRoute?
From 200 routes up, yes, in every test measured. FastRoute tries its routes in batches of about ten, so it slows down as routes are added: at 2,000 routes it takes 16.7 µs for a route with a parameter, where Dromos takes 0.83 µs. With 20 routes, FastRoute is up to 0.33 µs faster for routes with parameters and 404s.
Is Dromos faster than Laravel routing?
Yes, in every test measured. A request to a route with a parameter takes 41.1 µs in Laravel's router (illuminate/routing on its own) at 2,000 routes, against 0.83 µs in Dromos lean mode and 2.43 µs in full Dromos with PSR-7 requests and middleware. Laravel's router loads 75 files; Dromos loads 5 in lean mode.
Does Dromos support PSR-7, PSR-15 and PSR-17?
Yes. Its requests, responses, streams and URIs implement PSR-7 and pass the php-http PSR-7 integration tests. PSR-15 middleware from any package runs alongside Dromos middleware, and HttpFactory implements all six PSR-17 factories and passes the http-interop factory tests.
Can I use Dromos with OpenSwoole, RoadRunner or FrankenPHP?
Dromos runs in any long-running PHP worker: routes are compiled once and reused for every request. It ships an emitter for OpenSwoole, and you can implement EmitterInterface to send responses on other runtimes. It also runs under PHP-FPM, where its opcache route cache keeps routing to about 1.7 µs a request.
What does Dromos need to run?
PHP 8.2 or later. Its only dependencies are the four PSR interface packages: psr/http-message, psr/http-server-handler, psr/http-server-middleware and psr/http-factory. Install it with composer require pauldeano/dromos. It's free and MIT licensed.