Route::prefix

Prepends a URI segment to every route defined inside the group.

Signature
prefix(string $prefix): Illuminate\Routing\RouteRegistrar

Parameters

ParameterTypeDescription
$$prefixstringThe URI segment to prepend. Leading and trailing slashes are normalised, and the prefix may itself contain route parameters such as 'accounts/{account_id}'.

Return value

Illuminate\Routing\RouteRegistrarA RouteRegistrar, so the call chains into group() or into further attribute methods such as middleware() and name(). Chained onto a single route instead — Illuminate\Routing\Route::prefix() — it accepts string|null and returns that route.

Errors

Nothing. A prefix containing a parameter that no handler accepts simply arrives as an extra positional argument.

Route::prefix factors a shared URI segment out of a set of routes. Everything inside the group gains it, and nested groups append: prefix('admin') wrapping prefix('reports') yields /admin/reports/.... Slashes are inserted for you.

When to reach for it

An admin area, an API version, a tenant or locale segment, any block of routes sharing a common root. A prefix may contain parameters — prefix('accounts/{account_id}') — and that parameter is then passed to every handler in the group, ahead of the route's own parameters.

What trips people up

  • URIs only. A prefix does not touch route names. Route::prefix('admin') alone leaves the route named users, not admin.users; pair it with Route::name('admin.') when you want both.
  • routes/api.php already has one. The /api prefix is applied by withRouting(apiPrefix: ...), so adding prefix('api') inside that file produces /api/api/....
  • A prefixed parameter shifts argument positions. Every closure and controller method in the group must accept it first, or the wrong value lands in the wrong argument.
  • Prefixes cannot be removed by a nested group; they only accumulate.

Example

Example · php
<?php

use App\Http\Controllers\Admin\UserController;
use Illuminate\Support\Facades\Route;

// Prefix alone changes only the URI...
Route::prefix('admin')->group(function () {
    Route::get('/users', [UserController::class, 'index'])->name('users');
});

// ...so pair it with a name prefix to keep names in step.
Route::prefix('admin')->name('admin.')->group(function () {
    Route::get('/settings', [SettingsController::class, 'edit'])->name('settings');
});

echo route('users');
echo PHP_EOL;
echo route('admin.settings');
// http://example.com/admin/users
// http://example.com/admin/settings

When to use it

  • Put every administrative screen under /admin without repeating the segment.
  • Version an API by wrapping controllers in prefix('v1') with a matching name prefix.
  • Capture a tenant id in the path with prefix('accounts/{account_id}').
  • Nest a reports section inside an already-prefixed admin group.

Frequently asked questions

Does Route::prefix change route names too?

No, it only prepends to URIs. Use Route::name('admin.') alongside it if the route names should carry the same segment.

Can a route prefix contain a parameter?

Yes — Route::prefix('accounts/{account_id}'). The captured value is passed to every handler in the group as its first argument, before the route's own parameters.

Why are my API URLs coming out as /api/api/...?

routes/api.php is already loaded with an apiPrefix of 'api' from bootstrap/app.php, so adding ->prefix('api') inside that file applies it twice. Prefix with the version segment only.

More examples

A parameter inside the prefix

The generated URL shows both parameters filled in from the same array. The argument order is the detail that bites: {account_id} comes from the prefix and is therefore the first argument to every handler in the group, so a controller method written as show(Invoice $invoice) would silently receive the account id.

Example · php
<?php

use Illuminate\Support\Facades\Route;

Route::prefix('accounts/{account_id}')
    ->whereNumber('account_id')
    ->group(function () {
        // The prefix parameter arrives FIRST, before the route's own.
        Route::get('/invoices/{invoice}', function (string $accountId, string $invoice) {
            return $accountId.'/'.$invoice;
        })->name('invoices.show');
    });

echo route('invoices.show', ['account_id' => 42, 'invoice' => 7]);
// http://example.com/accounts/42/invoices/7

API versioning, and the api.php double-prefix trap

Two versions of the same resource coexist because each group carries a distinct URI prefix and a distinct name prefix — without the name prefixes the second apiResource would overwrite photos.index from the first. The /api segment in the output comes from bootstrap/app.php, not from these groups.

Example · php
<?php

use Illuminate\Support\Facades\Route;

// routes/api.php — /api is ALREADY applied by the framework.
// Do NOT write ->prefix('api') here; you would get /api/api/...
Route::prefix('v1')->name('api.v1.')->middleware('auth:sanctum')->group(function () {
    Route::apiResource('photos', PhotoController::class);
});

Route::prefix('v2')->name('api.v2.')->middleware('auth:sanctum')->group(function () {
    Route::apiResource('photos', V2\PhotoController::class);
});

echo route('api.v1.photos.index');
echo PHP_EOL;
echo route('api.v2.photos.show', ['photo' => 3]);
// http://example.com/api/v1/photos
// http://example.com/api/v2/photos/3

Discussion

  • Be the first to comment on this lesson.