Middleware
Middleware sits between the request and the controller. Every request passes through a chain of middleware, and each one can inspect the request, change it or stop it. This is how login checks, rate limiting and logging are done.
Creating
php artisan make:middleware EnsureIsAdminThe class appears in app/Http/Middleware.
The handle method
Middleware receives the request and the $next function, which passes the request further down the chain:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class EnsureIsAdmin
{
public function handle(Request $request, Closure $next)
{
if (! $request->user()?->is_admin) {
abort(403);
}
return $next($request);
}
}If the condition fails, the request never reaches the controller — the user sees a 403 error.
Attaching to a route
use App\Http\Middleware\EnsureIsAdmin;
Route::get('/admin', [AdminController::class, 'index'])
->middleware(EnsureIsAdmin::class);Route groups
To avoid repeating the middleware on every route, put the routes in a group:
Route::middleware(EnsureIsAdmin::class)->group(function () {
Route::get('/admin', [AdminController::class, 'index']);
Route::get('/admin/users', [AdminController::class, 'users']);
});Built-in middleware
Laravel's ready-made middleware have short names:
| Name | What it does |
|---|---|
auth | Lets in only logged-in users |
guest | Lets in only users who are not logged in |
throttle:60,1 | No more than 60 requests per minute |
verified | Requires a verified email address |
Route::get('/profile', [ProfileController::class, 'show'])->middleware('auth');Code after the response
Middleware can also act after the controller — for that, get the response from $next first:
public function handle(Request $request, Closure $next)
{
$response = $next($request);
$response->headers->set('X-App-Version', '1.0');
return $response;
}Common mistake
If you forget return $next($request);, the chain breaks: the controller is never called and the user gets an empty page. Every branch of handle() must either return the result of $next or stop the request explicitly — with abort() or redirect().