Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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 EnsureIsAdmin

The 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:

NameWhat it does
authLets in only logged-in users
guestLets in only users who are not logged in
throttle:60,1No more than 60 requests per minute
verifiedRequires 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().

Thanks for reading!

Donate to the site — we work hard for you.