Excluding Routes From a Catch-All Route by Using Regex in Laravel

Arie Visser • November 16, 2020

laravel php

When creating a catch-all route in Laravel, you might encounter conflicts with others routes in your application.

In this post I would like to describe how you can resolve some of these conflicts.

One use case for a catch-all route, is when you want to serve a single page application, for example with Vue.js, and let the JavaScript Framework handle all the routes.

You might end up with a route like this:

Route::any('/{any}', [AppController::class, 'show']);

While this definition will catch all routes, it will also cause the routes that are defined after the catch-all route to be ignored. For example, the API routes, or the route for an admin panel. You would also have to define the root route / separately.

This can be resolved by changing the order of your route definitions:

Route::get('/api', [ApiController::class, 'index']);
Route::get('/admin', [AdminController::class, 'index']);
Route::get('/', [AppController::class, 'show']);
Route::any('/{any}', [AppController::class, 'show']);

We use the PHP callable syntax for our route definitions. This makes it easier to navigate to the controller, and will help during refactoring in most IDEs.

There won't be any conflicts now. However, a cleaner solution would be to ignore the /admin, and /api routes in the catch-all route by adding a regular expression constraint, and make the any parameter optional:

Route::any('/{any?}', 'AppController@show')->where('any', '^((?!admin|api).)*$');
Route::get('/api', 'ApiController@show');
Route::get('/admin', 'AdminController@show');

This will give you the freedom to order your route definitions logically instead of functionally.

It also makes the definition of the root route unnecessary.