Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Laravel Middleware Real World Use Cases

By Kokil Thapa | Last reviewed: September 2026

Laravel middleware real world use cases sit at the centre of every production request: before your controller runs, middleware decides whether the caller is authenticated, which tenant they belong to, whether the API key is valid, and whether the request should be logged or throttled. On a typical Laravel web development project, middleware is where cross-cutting rules live so controllers stay focused on business logic. If you have shipped Laravel 12 or are planning a move to Laravel 13 on PHP 8.3+, understanding middleware registration, ordering, and failure modes is not optional—it is how you keep auth, payments, and multi-role portals predictable under load.

What is Laravel middleware and how does the request pipeline work?

Middleware is a filter layer. Each middleware class receives an HTTP request, may modify it, may short-circuit with a response, or passes control to the next closure in the stack. Laravel builds a pipeline: global middleware runs first, then group middleware (web or api), then route-specific middleware, and finally the controller or closure.

In Laravel 13 (minimum PHP 8.3), middleware is registered in bootstrap/app.php, not the old Http/Kernel.php file from Laravel 10 and earlier. That change matters when you upgrade from Laravel 12: the concepts are identical, but the registration API moved. The official Laravel 13 middleware documentation is the authoritative reference for method names and aliases.

Laravel Middleware PipelineHTTPRequestGlobalMiddlewareGroupweb / apiRouteAliasesControlleror ClosureHTTP ResponseBack through stackShort-circuit exits401 Unauthorized403 Forbidden429 Too Many RequestsMiddleware may return a response without reaching the controller
Laravel middleware real world use cases start in the request pipeline: global, group, and route layers run before any controller logic.

The handle method contract

Every middleware implements handle(Request $request, Closure $next). Call $next($request) to continue. Return a response directly to stop the pipeline. Terminable middleware also implements terminate(), which runs after the response is sent—useful for lightweight logging without blocking the user.

php artisan make:middleware EnsureUserIsActive

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnsureUserIsActive
{
    public function handle(Request $request, Closure $next): Response
    {
        if (! $request->user()?->is_active) {
            auth()->logout();

            return redirect('/login')
                ->with('error', 'Your account has been deactivated.');
        }

        return $next($request);
    }
}

This pattern appears constantly on client portals where admin staff disable accounts but sessions still exist in Redis or the database session driver. Middleware catches stale sessions before any document download route executes.

How do you register global, group, and route middleware in Laravel 13?

Laravel 13 centralises registration in bootstrap/app.php. You append to stacks, prepend when order matters, and define aliases for route files.

<?php

use App\Http\Middleware\EnsureUserIsActive;
use App\Http\Middleware\SetApplicationLocale;
use App\Http\Middleware\VerifyWebhookSignature;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->append(SetApplicationLocale::class);

        $middleware->alias([
            'active' => EnsureUserIsActive::class,
            'webhook.khalti' => VerifyWebhookSignature::class,
        ]);

        $middleware->group('api', [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })
    ->create();

Applying middleware on routes

Route groups keep definitions readable. On legal-tech portals I have maintained, public marketing pages use only the web group, authenticated client areas add auth and active, and admin panels add role middleware.

Route::middleware(['web', 'auth', 'active'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);
    Route::get('/documents/{document}', [DocumentController::class, 'show'])
        ->middleware('can:view,document');
});

Route::middleware('webhook.khalti')->post('/webhooks/khalti', KhaltiWebhookController::class);

Order matters when middleware depends on earlier layers. Authentication must run before role checks. Session middleware must run before CSRF validation on web routes. If you debug a 419 CSRF error on a form that worked yesterday, check whether a custom global middleware is reading the request body before the session starts—a mistake I have seen after adding request-logging middleware too early in the stack.

Three Middleware Registration LayersGlobalEvery requestTrustProxiesLocale, CORSMaintenanceGroupsweb or api stackSession + CSRFCookie encryptionSubstituteBindingsRoute AliasesPer route or groupauth, guestcan, throttleCustom aliasesRegistration file: bootstrap/app.phpappend(), prepend(), alias(), group()Laravel 12+ style — replaces Http/Kernel.phpPHP 8.3 minimum for Laravel 13
Global, group, and route middleware layers in Laravel 13 — where most Laravel middleware real world use cases get registered.
LayerTypical classesWhen to useExample route impact
GlobalTrustProxies, HandleCors, SetApplicationLocaleEvery HTTP hit, including webhooks and health checks/up, /webhooks/*, all pages
Group (web)StartSession, VerifyCsrfToken, EncryptCookiesBrowser sessions with forms and flash messagesBlade dashboards, checkout forms
Group (api)ThrottleRequests, SubstituteBindingsStateless JSON endpointsMobile app API, partner integrations
Route aliasauth, can:view,post, custom tenantFine-grained access on specific URIs/admin/*, /documents/{id}

What are the best Laravel middleware real world use cases for authentication and authorization?

Authentication answers who the caller is. Authorization answers what they may do. Middleware handles the first gate; policies and gates often handle object-level checks. For a deeper split, see the companion guide on Laravel policies and gates for authorization.

Session authentication on web portals

The built-in auth middleware redirects guests to login on web routes and returns 401 JSON on API routes when configured. On portals like client document-sharing systems, I combine auth with a custom active middleware and Spatie Laravel Permission role middleware for staff versus client roles.

Route::middleware(['web', 'auth', 'role:client|staff'])->prefix('portal')->group(function () {
    Route::get('/cases', [CaseController::class, 'index']);
    Route::post('/cases/{case}/upload', [DocumentController::class, 'store'])
        ->middleware('can:upload,case');
});

Keep role names in middleware aligned with your database seeder. A typo in role:adminstrator fails silently for authorised users and confuses everyone else with 403 responses.

Sanctum token authentication for APIs

For mobile or SPA backends, auth:sanctum validates bearer tokens. I use this on booking APIs and payment status endpoints where the frontend is Vue or Alpine calling a Laravel 12/13 backend. Compare token strategies in Passport vs Sanctum for API authentication.

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

Route::middleware(['auth:sanctum', 'ability:orders:read'])->get('/orders', [OrderController::class, 'index']);

Policy middleware for object-level access

can:view,document resolves the route parameter, loads the model, and calls the policy before the controller method runs. This is the correct place to stop a user guessing /documents/1042 when they own document 7. Controllers should assume authorization already passed; duplicating checks in the controller creates drift.

Auth Middleware Stack — Client PortalLogin requestauthSession valid?activeAccount on?rolecan:view,documentPolicy checks ownershipController actionStream PDF to client403 / RedirectFailed authInactive userWrong roleLayered middleware on law-firm and booking portals
Real-world auth middleware chain: session, active account, role, and policy before document access.

How do you build custom middleware for APIs, rate limiting, and multi-tenant apps?

API-facing projects—digital commerce platforms, directory sites, trek booking engines—lean on middleware for throttling, tenant scoping, and idempotency. These concerns belong in middleware because they inspect the request envelope, not domain aggregates.

Rate limiting and abuse prevention

Laravel ships throttle middleware backed by Redis 8.10 or the cache driver. For public login routes, tight limits reduce credential stuffing. For partner APIs, define named limiters in a service provider.

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;

RateLimiter::for('api', function (Request $request) {
    return Limit::perMinute(120)->by($request->user()?->id ?: $request->ip());
});

RateLimiter::for('login', function (Request $request) {
    return Limit::perMinute(5)->by($request->ip());
});
Route::middleware(['throttle:login'])->post('/login', [AuthController::class, 'store']);
Route::middleware(['auth:sanctum', 'throttle:api'])->apiResource('products', ProductController::class);

Read Laravel API best practices for pagination and error shapes that pair well with throttle headers.

Tenant isolation middleware

On multi-vendor marketplaces, a IdentifyTenant middleware reads subdomain or X-Tenant-ID, validates membership, and binds a tenant instance into the container or request attributes. Controllers and Eloquent global scopes read that context. Getting this wrong leaks rows across vendors—a production incident class I treat as P0.

public function handle(Request $request, Closure $next): Response
{
    $tenant = Tenant::where('slug', $request->route('tenant'))->first();

    if (! $tenant || ! $tenant->is_active) {
        abort(404);
    }

    if ($request->user() && ! $request->user()->belongsToTenant($tenant)) {
        abort(403);
    }

    app()->instance(Tenant::class, $tenant);
    $request->attributes->set('tenant_id', $tenant->id);

    return $next($request);
}

Webhook signature verification

Payment gateways—Khalti, eSewa, Stripe—POST callbacks to public URLs. Never trust the body without verifying HMAC or RSA signatures first. Dedicated middleware keeps verification out of fat controllers and guarantees every webhook route behaves identically. See Khalti integration for Laravel apps for gateway-specific headers.

public function handle(Request $request, Closure $next): Response
{
    $signature = $request->header('Khalti-Signature');
    $payload = $request->getContent();

    $expected = hash_hmac('sha256', $payload, config('services.khalti.webhook_secret'));

    if (! hash_equals($expected, (string) $signature)) {
        return response()->json(['message' => 'Invalid signature'], 401);
    }

    return $next($request);
}

Return 401 on bad signatures, 200 only after the controller queues idempotent processing. Payment middleware should never redirect to a login page—that breaks gateway retry logic documented in each provider's webhook guide.

Locale, timezone, and Nepal-specific context

Sites serving Nepali and English audiences often set locale from session, user preference, or Accept-Language. Middleware is the right layer because Blade layouts and validation messages depend on it before the controller renders. For Bikram Sambat display, middleware can attach a calendar helper to the request while conversion math lives in a dedicated service—similar separation to what you would use with a Nepali date converter tool on the front end.

public function handle(Request $request, Closure $next): Response
{
    $locale = $request->user()?->locale
        ?? $request->session()->get('locale', config('app.locale'));

    if (in_array($locale, ['en', 'ne'], true)) {
        app()->setLocale($locale);
    }

    return $next($request);
}
API Middleware — eCommerce ExampleAPI Clientthrottle120/minSanctumBearer tokentenantScope dataOrderControllerCreate order + queue jobParallel webhook routeNo auth sessionwebhook.khalti middlewareSignature verify onlyRedis + QueueRate limit countersIdempotent webhook jobsMySQL 9.7 / 8.4 orders
API and webhook middleware stacks on a Laravel eCommerce app — throttle, token auth, tenant scope, and signed callbacks.

When should you avoid middleware—and what belongs elsewhere?

Middleware excels at request-bound, synchronous checks with no heavy I/O. Push these out of middleware:

  • Complex business validation — use Form Requests. Price rules, inventory checks, and VAT calculations belong in validated request objects, not middleware.
  • Long external API calls — middleware blocks the entire pipeline. Queue a job from the controller or a queued listener; see Laravel events and listeners in production.
  • Database-heavy reporting authorisation — a policy that runs three joins on every list page belongs in a cached permission bit or a dedicated query scope invoked from the controller.
  • Response transformation — use API resources or view composers instead of middleware that mutates JSON after the controller.

A common mistake is stuffing SEO logic into middleware. Canonical URLs and meta tags belong in controllers, view models, or packages like artesaos/seotools—not global middleware that runs on webhook routes. Technical SEO for Laravel is covered in SEO setup for Laravel sites.

Testing middleware in isolation

Use HTTP feature tests asserting status codes and headers. For unit-level tests, call handle() with a mocked request.

public function test_inactive_users_are_logged_out(): void
{
    $user = User::factory()->inactive()->create();
    $this->actingAs($user);

    $response = $this->get('/dashboard');

    $response->assertRedirect('/login');
    $this->assertGuest();
}

On CI pipelines I maintain with GitLab CI and Deployer 7, middleware regressions are cheap to catch if routes stay covered. A broken VerifyCsrfToken exception list deploys faster than a broken payment rule—test both.

Performance and ordering checklist

  1. Keep global middleware minimal; every route pays the cost.
  2. Place TrustProxies early when behind Cloudflare or a load balancer so $request->ip() is correct for throttles.
  3. Use Redis for throttle and session drivers on multi-node setups.
  4. Exclude webhook and health routes from session middleware when possible.
  5. Reload PHP-FPM after deploy so opcache picks up middleware class changes—stale opcache manifests as "middleware not running" after symlink swap.

For enterprise modules with strict audit requirements, pair middleware logging with structured log channels rather than dd() in production. Enterprise Laravel application development often adds immutable audit middleware that writes actor, route, and IP before sensitive exports—a pattern I have used on document portals where compliance matters more than microsecond latency.

If you are building a greenfield API, align middleware design with your versioning strategy early; retrofits hurt. Laravel API versioning and building RESTful APIs with Laravel both assume a consistent auth and throttle layer at the edge.

Need to prototype middleware regex for URI exclusions? A regex tester saves time when configuring VerifyCsrfToken::$except or custom path matchers—just do not paste production secrets into online tools on untrusted networks.

Architecture discussions often overlap with broader patterns. Modern Laravel architecture places middleware at the HTTP boundary while domain services stay framework-agnostic—a boundary worth preserving when you outgrow a single routes/web.php file.

For teams comparing frameworks, Symfony's HTTP kernel uses a similar event-driven pipeline; the mental model transfers even if registration differs. Laravel 13 on PHP 8.5 with Composer 2.10 remains my default stack for Nepali businesses that need operational web apps, not demos—middleware is a big reason why: auth, locale, throttling, and webhooks stay consistent across booking systems, gift-card commerce, and legal intake forms.

When upgrading from Laravel 11 (EOL March 2026) or Laravel 12 (supported to February 2027), re-audit custom middleware namespaces and deprecated method signatures against the official Laravel 13 upgrade guide. PHP's typed property and return-type requirements in 8.3+ will surface latent middleware bugs that PHP 8.2 tolerated.

External reference for HTTP semantics on status codes middleware returns: the MDN HTTP response status documentation clarifies when to use 401 versus 403—a distinction middleware authors get wrong surprisingly often.

Key Takeaways

  • Register middleware in bootstrap/app.php for Laravel 13: global for cross-cutting concerns, groups for web/api, aliases for route-level gates.
  • Use middleware for auth, active-account checks, roles, throttling, tenant binding, webhook signatures, and locale—not for heavy business rules.
  • Order stacks deliberately: session before CSRF, authentication before can policy middleware.
  • Keep webhook routes on dedicated middleware without session or CSRF; verify signatures before controllers touch payment state.
  • Test middleware with feature tests on real routes; inactive-user and cross-tenant access cases catch production leaks early.
  • After deploy, confirm PHP-FPM/opcache reload so middleware changes actually run—especially on symlink-based release workflows.

People Also Ask

What is the difference between middleware and a Form Request in Laravel?

Middleware runs on every matching route before routing parameters are fully resolved into validated input. Form Requests validate and authorise input for a single controller action after routing. Use middleware for "is this user allowed to hit this area at all?" and Form Requests for "is this payload valid for this action?"

Can Laravel middleware modify the response after the controller runs?

Standard middleware wraps the downstream pipeline: you can mutate the response returned from $next($request). Terminable middleware adds a terminate() method that runs after the response is sent to the client—ideal for logging and metrics without adding latency to the user.

How do you exclude routes from CSRF middleware?

Publish or extend App\Http\Middleware\VerifyCsrfToken and add URI patterns to the $except array—typically webhook endpoints and third-party callbacks. Prefer explicit paths over wildcards that accidentally expose state-changing routes.

Does Laravel middleware run for Artisan commands or queued jobs?

No. Middleware is HTTP-only. Console commands and jobs use their own bootstrapping. If you need the same tenant or auth context in a job, pass IDs explicitly or restore context in the job's handle() method rather than expecting middleware to run.

Ship middleware that matches your business rules

Laravel middleware real world use cases are the guardrails around everything your users and integrations touch—sessions on a law-firm portal, bearer tokens on a mobile API, signed callbacks from Khalti or Stripe. Get the pipeline right once and every new route inherits the same rules. If you are planning a Laravel 13 upgrade, a multi-role portal, or an API layer for an existing app, I can help design and audit the middleware stack alongside deployment and testing. Review production Laravel portfolio work, explore custom software development services, or contact us to discuss your project.

Frequently Asked Questions

Laravel middleware is a filter layer in the HTTP request pipeline. Each class receives the request, may modify it, short-circuit with a response, or pass control to the next closure before your controller runs.

Session auth, Sanctum API tokens, role checks, rate limiting, tenant scoping, webhook signature validation, locale selection, and request logging—registered globally, on route groups, or as single-route aliases.

Laravel 13 centralises registration in bootstrap/app.php using the withMiddleware callback. You append to stacks, prepend when order matters, define aliases for route files, and configure group middleware for web and api—replacing the old Http/Kernel.php approach from Laravel 10 and earlier.

Global middleware runs first on every HTTP hit, then group middleware such as web or api, then route-specific middleware, and finally the controller or closure. Cross-cutting rules like authentication, CSRF, and throttling execute in this order before business logic. Authentication must run before role checks, and session middleware must run before CSRF validation on web routes.

Every middleware implements handle(Request $request, Closure $next). Call $next($request) to continue the pipeline. Return a response directly to stop execution. Terminable middleware also implements terminate(), which runs after the response is sent—useful for lightweight logging without blocking the user.

Run php artisan make:middleware EnsureUserIsActive, then implement handle() to check conditions before calling $next($request). The article's inactive-account example logs out deactivated users and redirects to login—a pattern common on client portals where admin staff disable accounts but sessions still exist in Redis or the database session driver.

Use Route::middleware() on groups or individual routes. A typical portal stack combines web, auth, and active for authenticated areas, adds role middleware for staff versus client access, and applies can:view,document on sensitive document routes. Register custom classes as aliases in bootstrap/app.php, then reference them by short name on routes.

Authentication middleware answers who the caller is; authorization middleware answers what they may do. Combine built-in auth with custom active middleware and Spatie Laravel Permission role middleware on portals. Use auth:sanctum for API bearer tokens and can:view,model for object-level policy checks so users cannot guess URLs for resources they do not own.

Apply auth:sanctum to validate bearer tokens on stateless JSON endpoints. For finer control, combine it with ability middleware such as ability:orders:read on specific routes. This pattern suits mobile apps, Vue or Alpine SPAs, and booking or payment status APIs where the frontend calls a Laravel 12 or 13 backend without browser sessions.

Laravel ships throttle middleware backed by Redis 8.10 or your cache driver. Define named limiters in a service provider—tight limits on login routes reduce credential stuffing, while higher per-minute limits on authenticated API routes protect partner integrations. Apply throttle:login or throttle:api on routes and read throttle response headers for client-side backoff.

Create IdentifyTenant middleware that reads subdomain or X-Tenant-ID, validates the tenant is active, confirms the authenticated user belongs to that tenant, then binds the tenant instance into the container and sets tenant_id on request attributes. Controllers and Eloquent global scopes read that context. Getting this wrong leaks rows across vendors—a production incident class to treat as P0.

Dedicated middleware reads the provider signature header, computes HMAC against the raw request body using your webhook secret, and compares with hash_equals. Return 401 on invalid signatures; return 200 only after the controller queues idempotent processing. Never redirect webhook routes to a login page—that breaks gateway retry logic for Khalti, eSewa, and Stripe callbacks.

Middleware reads locale from the authenticated user, session, or Accept-Language header, validates against allowed values such as en and ne, then calls app()->setLocale() before the controller renders. This ensures Blade layouts and validation messages use the correct language. For Bikram Sambat display, attach a calendar helper to the request while keeping conversion math in a dedicated service.

Push complex business validation into Form Requests, long external API calls into queued jobs, database-heavy reporting authorisation into cached permission bits or controller scopes, and response transformation into API resources or view composers. Avoid stuffing SEO logic such as canonical URLs into global middleware that also runs on webhook routes—use controllers, view models, or packages like artesaos/seotools instead.

Use HTTP feature tests asserting status codes, redirects, and headers—for example, verify inactive users hitting /dashboard are logged out and redirected to login. On CI pipelines with GitLab CI and Deployer 7, route coverage catches middleware regressions cheaply. Also test CSRF exception lists alongside payment rules, and reload PHP-FPM after deploy so opcache picks up middleware class changes after symlink swap.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: