Originally published on laravel.learnio.dev — part of the Laravel best practices course. View this lesson on the original site.

Filter requests before controller code runs

Middleware sits between incoming HTTP requests and your controller actions. It can allow, modify, or reject requests early.

What middleware is used for

Common middleware jobs:

  • check authentication,
  • verify CSRF tokens (web routes),
  • apply throttling or rate limits,
  • attach tenant or locale context,
  • block access based on app state.

This keeps controllers focused on business orchestration.

Route-level and group-level assignment

Single route:

Route::get('/account', [AccountController::class, 'show'])
    ->middleware('auth');

Route group:

Route::middleware(['auth', 'verified'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
    Route::get('/billing', [BillingController::class, 'index']);
});

Middleware runs in the order listed.

Web and API defaults

  • routes/web.php uses the web middleware group by default.
  • routes/api.php uses the api middleware group by default.

That default split helps you keep browser and API concerns separate.

Bad way vs better way

Bad way (auth check duplicated in each controller):

public function index()
{
    if (! auth()->check()) {
        return redirect('/login');
    }

    // action logic...
}

Better way (single middleware boundary):

Route::middleware('auth')->group(function () {
    Route::get('/reports', [ReportController::class, 'index']);
});

Why better:

  • one reusable access boundary,
  • no repeated auth checks in controllers,
  • easier to audit and test request flow.

Debug middleware assignment quickly

php artisan route:list -v --path=dashboard

-v shows middleware stacks so you can verify what is actually applied.

Middleware pipeline flow

flowchart LR
  A[Request] --> B[Middleware 1]
  B --> C[Middleware 2]
  C --> D[Controller action]
  D --> E[Response]

Next, you will use middleware together with redirects to shape cleaner request outcomes for both browser and API flows.

From Laravel best practices — Routing, Controllers & Request Flow