Middleware provides a convenient, modular mechanism for inspecting and filtering HTTP requests entering your application. For example, Laravel includes built-in middleware that verifies the user is authenticated, ensures CSRF tokens match, checks whether the application is in maintenance mode, and trims trailing whitespace from input fields.
Figure 12.1: The Onion Architecture — Requests Passing Inward Through Middleware Layers
To create a new middleware that restricts access strictly to administrators, run:
php artisan make:middleware EnsureUserIsAdmin
Open the newly created class in app/Http/Middleware/EnsureUserIsAdmin.php:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class EnsureUserIsAdmin
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next): Response
{
// Check if user is logged in AND possesses admin privileges
if (!$request->user() || !$request->user()->is_admin) {
// Abort with HTTP 403 Forbidden
abort(403, 'Unauthorized access: Administrator credentials required.');
}
// Pass request inward to the next middleware or controller
return $next($request);
}
}
In Laravel 11, register your custom middleware aliases or prepend/append to global pipelines inside bootstrap/app.php:
use App\Http\Middleware\EnsureUserIsAdmin;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
)
->withMiddleware(function ($middleware) {
// Register route alias
$middleware->alias([
'admin' => EnsureUserIsAdmin::class,
]);
// Append to global web stack if desired:
// $middleware->web(append: [CustomTrackingMiddleware::class]);
})->create();
Now you can attach the admin middleware to any route or route group in routes/web.php:
Route::get('/admin/server-logs', [AdminController::class, 'logs'])
->middleware(['auth', 'admin']);
Depending on whether your logic runs before or after the controller executes, structure the $next($request) call accordingly:
public function handle($request, Closure $next)
{
// 1. Perform security/IP checks first
if ($this->isIpBlocked($request->ip())) {
abort(403);
}
// 2. Pass to controller
return $next($request);
}
public function handle($request, Closure $next)
{
// 1. Execute controller first
$response = $next($request);
// 2. Modify outgoing headers
$response->header('X-Frame-Options', 'DENY');
return $response;
}
If your middleware defines a public function terminate($request, $response) method, Laravel automatically invokes it after the HTTP response has already been sent to the user's browser. This is perfect for writing analytics to databases or dispatching non-critical log tasks without slowing down the user's page load!
Broaden your backend architecture, REST API design, and enterprise production engineering expertise:
Implement security headers, CORS, and rate limiting with middleware.
Protect private routes with auth and verified middleware filters.
Attach custom middleware pipelines to route groups and controllers.
Explain the Onion Architecture and $next($request) pipeline in oral exams.