In Laravel's bootstrap/app.php, how do you add middleware to the web or api group or define a named group, and what does group() do differently?
answer
- web() and api() take named arguments
- append: and prepend: arrays
- appendToGroup creates the group if missing
- group() defines the whole list
- a group may list another group
basics
~10 sInside withMiddleware(), $middleware->web(append: [...]) or api(prepend: [...]) extends the built-in groups, and appendToGroup() or prependToGroup() extends or creates any named group. group('name', [...]) sets the entire list, so group('web', ...) replaces the framework's defaults.
solid answer
~30 s`$middleware->web(append: [TrackAgentReferral::class])` and `$middleware->api(prepend: [...])` are shortcuts for `appendToGroup('web', ...)` and `prependToGroup('api', ...)`. The same two methods work on any name, so `appendToGroup('back-office', [EnsureAgentIsVerified::class, LogAgentActions::class])` creates a `back-office` group that routes reference with `->middleware('back-office')`. `group('back-office', [...])` instead defines the list wholesale, and if you call it for `web` or `api` it replaces the framework's default entries, so you must restate the session, cookie and CSRF middleware yourself. Appends and prepends are applied after that definition regardless of call order, entries are de-duplicated, and a group may include another group by name; a group that names itself throws a `LogicException`.
code
php · 16 lines<?php
use App\Http\Middleware\EnsureAgentIsVerified;
use App\Http\Middleware\LogAgentActions;
use App\Http\Middleware\TrackAgentReferral;
use Illuminate\Foundation\Configuration\Middleware;
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(append: [TrackAgentReferral::class]);
$middleware->appendToGroup('back-office', [
EnsureAgentIsVerified::class,
LogAgentActions::class,
]);
})go deeper
Recall that web() and api() take append: and prepend: arrays, and that routes use a group by passing its name to ->middleware().
Explain the difference between extending a group and redefining it with group(), and that the configurator applies appends after any redefinition.
Show judgement about which layer a concern belongs in, and why redefining web wholesale is a maintenance risk when the framework's defaults change on upgrade.
Frame named groups as the auditable unit of policy per site area, and set a rule that built-in groups are extended rather than redefined.
## What a middleware group is in Laravel A **middleware group** is a named list of middleware that routes reference with one string. `->middleware('back-office')` on a route expands, at dispatch time, into every class in the `back-office` group, in order. Laravel ships two groups: - **`web`**, which starts the session, encrypts cookies, shares validation errors with views, checks CSRF tokens and resolves route model bindings. `withRouting(web: ...)` wraps `routes/web.php` in it. - **`api`**, which by default only resolves route bindings. `withRouting(api: ...)`, added by `php artisan install:api`, wraps `routes/api.php` in it and adds the `/api` URI prefix. All group configuration happens in `bootstrap/app.php`, inside the `withMiddleware()` closure, on the `Illuminate\Foundation\Configuration\Middleware` object. ## Extending the built-in groups: web() and api() `web()` and `api()` take four named arguments: `append`, `prepend`, `remove` and `replace`. The first two are the everyday ones: ```php ->withMiddleware(function (Middleware $middleware): void { $middleware->web(append: [TrackAgentReferral::class]); $middleware->api(prepend: [EnsureTravelPartnerKey::class]); }) ``` - `append:` places the classes **after** the group's existing entries, so a web-appended middleware sees a started session and resolved route bindings. - `prepend:` places them **before** the existing entries, so a web-prepended middleware runs before the session is started. - Both are shortcuts: `web(append: X)` calls `appendToGroup('web', X)` and `api(prepend: Y)` calls `prependToGroup('api', Y)`. - Passing a class that is already in the group does not duplicate it; the merged list goes through `array_unique`. ## Custom named groups: appendToGroup(), prependToGroup(), group() For a family of routes that share several checks, a named group avoids repeating the list on each route. On a travel-agency site the agent back office might need three: ```php $middleware->appendToGroup('back-office', [ EnsureAgentIsVerified::class, LogAgentActions::class, ]); ``` `appendToGroup()` and `prependToGroup()` **create** the group if it does not exist yet, starting from an empty list. `group()` is different in kind: | Call | Effect on an existing group | Effect on a new name | |---|---|---| | `appendToGroup('x', [...])` | Adds entries after the current ones | Creates `x` with these entries | | `prependToGroup('x', [...])` | Adds entries before the current ones | Creates `x` with these entries | | `group('x', [...])` | **Replaces** the whole definition | Creates `x` with exactly this list | Because `group()` replaces, `group('web', [SetLocale::class])` leaves the web group with one entry: no session, no cookie encryption, no CSRF check. The documented use of `group('web', ...)` is to take manual control by restating the full default list and editing it; removing or swapping one default entry is a separate, narrower tool. ## The order the configurator applies your calls The configurator does not apply calls in the order you write them. When the HTTP kernel is resolved it builds each group in fixed stages: 1. Start from the framework defaults for `web` and `api`. 2. Overlay every `group()` definition by name, so a redefined `web` wins over the default. 3. Apply replacements, then removals. 4. Apply `prependToGroup()` entries, then `appendToGroup()` entries, each de-duplicated. So `$middleware->web(append: [TrackAgentReferral::class])` still lands in a `web` group you redefined with `group()`, whether you wrote it before or after the redefinition. ## Nesting and the self-reference error A group entry may be the **name of another group**. When a route uses the outer group, `MiddlewareNameResolver` expands the inner group in place, recursively. This lets a `partner-api` group include `api` and add its own checks. A group that lists **its own name** throws `LogicException` with the message `[back-office] middleware group is referencing itself.` when a route using it is resolved. ## Where the groups are attached Groups do nothing until something references them. `withRouting()` attaches `web` to `routes/web.php` and `api` to `routes/api.php` for you; any custom group is attached explicitly, on a route, on a route group, or inside another group. The web group's contents are the framework's defaults plus your appends and prepends, so after an upgrade the defaults can change underneath an extended group without any edit on your side, while a group redefined with `group('web', ...)` stays frozen at whatever list you wrote. ## When to reach for which - A concern every browser page needs: `web(append: ...)`. - A concern every API endpoint needs: `api(append: ...)` or `api(prepend: ...)`. - A bundle shared by one area of the site: a named group via `appendToGroup()`. - Full control of a built-in group's contents: `group('web', [...])` with the whole list restated.
- If a route in routes/web.php also uses ->middleware('back-office'), and back-office repeats a class already in web, does it run twice?No. The router gathers the route's middleware, expands the groups into class names and passes the list through `Router::uniqueMiddleware()`, which keeps the first occurrence of each string. The duplicate is dropped, so the class runs once, at its first position.
- Why might $middleware->web(prepend: [SetLocale::class]) break a middleware that reads the locale from the session?Prepending puts it ahead of `StartSession` in the web group, so when it runs the session has not been started and `$request->session()` is not available. It needs to sit after the session middleware, which `web(append: ...)` gives you.
saying these in an interview costs you the question
- Uses group('web', [...]) to add one class and expects the defaults to remain
- Believes appendToGroup() fails when the group does not exist yet
- Thinks web(append:) also adds the class to routes/api.php
- Assumes the order of calls in withMiddleware() decides whether group() or appendToGroup() wins
- Expects a duplicated class to run twice when two groups list it