nikic/FastRoute: a regex router for PHP that compiles routes to a dispatch table
Fast request router for PHP
At a glance
- What is it?
- FastRoute is a standalone PHP routing library for people who already own their request handling. It compiles route definitions into a data structure you dispatch against, and it deliberately stops there.
- Who is it for?
- Adopt FastRoute when you control the request lifecycle and want routing as a library rather than a framework: dispatch() returns a status code and your handler data, and nothing else happens behind your back. Do not adopt it if you expect middleware, PSR-7 request objects, or automatic controller invocation, because the README assigns all of that to you.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 82 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem FastRoute solves, and who is holding the request
Most PHP routing code sits inside a framework that owns the request object, the response object, and the middleware stack. FastRoute takes the opposite position. It is a library that answers one question: given an HTTP method and a URI path, which handler did I register for it? The README is explicit that this is the whole contract. The $handler value "does not necessarily have to be a callback, it could also be a controller class name or any other kind of data you wish to associate with the route," and "FastRoute only tells you which handler corresponds to your URI, how you interpret it is up to you."
That makes it a fit for micro-frameworks, legacy applications being slowly refactored, CLI tools that need URL-shaped command matching, and anyone building a router as a component rather than consuming one. It is a poor fit for a team that wants routing plus request parsing plus dependency injection wired together, because none of that is here. The library is not bound to the PHP web SAPIs either; the README states that fetching and normalizing the method and URI is your job. If you cannot say where $_SERVER['REQUEST_URI'] gets decoded in your codebase, you are not the target user yet.
How the dispatcher turns route patterns into a lookup
Routes are registered through a callable that receives a FastRoute\ConfigureRoutes instance. The pattern syntax uses braces for placeholders: {foo} matches [^/]+ by default, and {bar:[0-9]+} supplies a custom regex. There is a constraint worth internalizing early, because it produces confusing failures: "Custom patterns for route placeholders cannot use capturing groups." The README gives the fix directly. {lang:(en|de)} is invalid, while {lang:en|de} and {lang:(?:en|de)} both work. A non-capturing group is fine; a capturing one is not.
Optional segments use square brackets and are only supported in a trailing position. /user/{id:\d+}[/{name}] is equivalent to registering two separate routes, and nesting works: /user[/{id:\d+}[/{name}]]. The invalid case is documented too, /user[/{id:\d+}]/{name}, because the optional part sits in the middle. Groups provide a prefix mechanism, and nested groups combine their prefixes.
Dispatch returns an array whose first element is a status code drawn from Dispatcher::NOT_FOUND, Dispatcher::METHOD_NOT_ALLOWED, or Dispatcher::FOUND. On method-not-allowed the second element holds the allowed methods, which is what you need to emit a correct Allow header. On found, the second element is the handler and the third is the array of captured variables. The README does not describe the internal data structure beyond pointing at a blog post explaining the implementation, so treat the compiled table as an implementation detail rather than something to depend on.
Installing FastRoute and dispatching a first request
Installation is a single Composer command. The README states the requirement plainly: PHP 8.1 or newer.
composer require nikic/fast-routeThe README's basic example builds a dispatcher from a closure and dispatches against values pulled from $_SERVER. Note the manual query-string stripping and rawurldecode call, both of which are the caller's responsibility.
<?php
require '/path/to/vendor/autoload.php';
$dispatcher = FastRoute\simpleDispatcher(function(FastRoute\ConfigureRoutes $r) {
$r->addRoute('GET', '/users', 'get_all_users_handler');
$r->addRoute('GET', '/user/{id:\d+}', 'get_user_handler');
$r->addRoute('GET', '/articles/{id:\d+}[/{title}]', 'get_article_handler');
});The dispatch call returns a three-state array, and the switch in the README is the shape most applications end up with. For the method-not-allowed branch, $routeInfo[1] carries the allowed methods.
$routeInfo = $dispatcher->dispatch($httpMethod, $uri);
switch ($routeInfo[0]) {
case FastRoute\Dispatcher::NOT_FOUND:
// ... 404 Not Found
break;
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
$allowedMethods = $routeInfo[1];
break;
case FastRoute\Dispatcher::FOUND:
$handler = $routeInfo[1];
$vars = $routeInfo[2];
break;
}For production, swap simpleDispatcher for cachedDispatcher. The options array requires a cacheKey, and cacheDriver defaults to FastRoute\Cache\FileCache. The README also shows cacheDisabled, which is enabled by default, and notes that cacheFile still works for v1 compatibility.
$dispatcher = FastRoute\cachedDispatcher(function(FastRoute\ConfigureRoutes $r) {
$r->addRoute('GET', '/user/{name}/{id:[0-9]+}', 'handler0');
}, [
'cacheKey' => __DIR__ . '/route.cache',
'cacheDisabled' => IS_DEBUG_ENABLED,
]);Where FastRoute stops being the right tool
The library has no middleware concept. The related searches suggest people look for "Fastroute middleware", but the README never mentions middleware, PSR-15, or a pipeline. If your routing decisions depend on authentication state, headers, or a request object, you are building that layer yourself on top of the dispatch result. That is a design boundary, not an oversight, but it means the effort estimate for a real application is larger than the install command suggests.
Optional segments are the second trap. They must be trailing, so a route shape like /user[/{id:\d+}]/{name} is rejected. Teams migrating from frameworks that allow mid-path optionality will need to enumerate the combinations as separate routes instead.
Capturing groups inside placeholder patterns are the third. The error surfaces when the compiled pattern misbehaves rather than at registration, which is a worse debugging experience than a validation exception would be. The README documents the rule and the two workarounds, so the information exists, but it is easy to skim past.
Finally, consider release timing. The most recent stable release listed is v1.3.0 from 2018-02-13, while 2.0.0-beta1 dates from 2024-03-04. The README's examples use the 2.x API, including the ConfigureRoutes type and the cacheKey option, so anyone following the README is effectively on the beta line. The README does not document a rollback path between the two, and no migration guide appears among the available files.
FastRoute against a full framework router such as Mezzio
The related searches pair FastRoute with Mezzio, which is the honest comparison. Mezzio's routing layer consumes FastRoute as its default router and wraps it with PSR-7 request handling, middleware, and container-based handler resolution. The difference is not speed or pattern syntax; it is where the boundary sits. With FastRoute alone you write the switch statement from the README and decide what a handler string means. With Mezzio you register a handler class and the framework instantiates it.
Choosing FastRoute over that stack means accepting more wiring in exchange for fewer assumptions about your application structure. It is a reasonable trade for a codebase that already has its own request abstraction, and a poor one for a greenfield project where the team has no opinion about request objects yet. The other axis is coupling: FastRoute can be dropped into a legacy front controller without restructuring anything, which a full framework cannot. That is the case the library was built for.
Maintenance, upgrades and the licence question
The repository is not archived, and the last push was on 2026-07-09. The Makefile shows the project's own quality gates: phpunit, phpcs with parallel execution, phpstan analyse with --memory-limit=-1, and phpbench with --retry-threshold=15. A contributor running make all executes all of them. That tells you the maintenance surface is real, but it also tells you the upgrade cost for a consumer: the 2.x line changes the route-definition API from the collector style used in 1.x to the ConfigureRoutes callable shown in the README, and the cache option naming moved from cacheFile to cacheKey with cacheFile retained only for v1 compatibility.
On licensing, the repository carries a LICENSE file at the top level, but the metadata records the licence as NOASSERTION, meaning no standard identifier was matched. Read that file before you redistribute the library inside a product; this is a packaging question, not a legal opinion, and the licence text is the only authority.
Caching is the other ongoing cost. cachedDispatcher writes compiled routing data to a cacheKey path, so any deployment that ships code without a writable cache directory will fall back to regenerating routes. The README shows cacheDisabled defaulting to enabled, which means the cache is on unless you turn it off.
Editorial conclusion
Adopt FastRoute when you control the request lifecycle and want routing as a library rather than a framework: dispatch() returns a status code and your handler data, and nothing else happens behind your back. Do not adopt it if you expect middleware, PSR-7 request objects, or automatic controller invocation, because the README assigns all of that to you. Before committing, verify that your PHP version satisfies the 8.1 floor, that your route placeholders avoid capturing groups, and that optional segments appear only in trailing position. Note also that the stable release line is v1.3.0 from 2018 while 2.0.0-beta1 dates from 2024, and the README's examples use the 2.x ConfigureRoutes API.
Frequently asked questions
What is FastRoute used for in PHP?
It maps an HTTP method and URI path to a handler you registered, returning one of three dispatch statuses. The handler can be a callback, a controller class name, or any other data, and interpreting it is left to your code.
How do I install FastRoute?
Run composer require nikic/fast-route. The README states the library requires PHP 8.1 or newer.
Can FastRoute route placeholders use capturing groups?
No. Custom patterns for placeholders cannot use capturing groups, so {lang:(en|de)} is invalid while {lang:en|de} and {lang:(?:en|de)} are accepted.
Does FastRoute support optional URL segments in the middle of a route?
No. Optional parts written in square brackets are only supported in a trailing position, and the README marks /user[/{id:\d+}]/{name} as invalid.
How does FastRoute caching work?
Use cachedDispatcher instead of simpleDispatcher and pass an options array with a required cacheKey, which is the file location when using the default file cache. The cacheDriver option defaults to FastRoute\Cache\FileCache.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nikic-fastroute)