What is middleware in Laravel?

Middleware in Laravel

The Middleware provides an easy way to inspect and filter HTTP requests that enter our application. In other words, Middleware in Laravel acts as a bridge between HTTP requests and responses

That is, in simple words, as an example of middleware, it can be said that if a user wants to browse a website page, then the middleware can check whether this user has the right to access this page and if so, then the user can access that page otherwise the user will be redirected to a page mentioning that this page cannot be accessed.

Middlewares in Laravel are stored in the app/Http/Middleware path.

In-built middleware of Laravel

With the installation of Laravel 10, we will get the following in-built middlewares available on our system. Now we will know about these middleware one by one

Authenticate middleware

This middleware verifies whether the user of our application is authenticated or not. The path of this middleware is app/Http/Middleware/Authenticate.php.

EncryptCookies middleware

This middleware allows us to exclude certain cookies from being encrypted. The path of this middleware is app/Http/Middleware/EncryptCookies.php

PreventRequestsDuringMaintenance middleware

This middleware is used to control whether a URL will be opened as an exception when Laravel is in maintenance mode. The path of this middleware is app/Http/Middleware/PreventRequestsDuringMaintenance.php

RedirectIfAuthenticated middleware

This is used to prevent an already authenticated user from accessing the login or registration routes or views since they have already been logged in. The path of this middleware is app/Http/Middleware/RedirectIfAuthenticated.php

TrimStrings middleware

This is used to remove whitespace characters from all the inputs. The path of this middleware is app/Http/Middleware/TrimStrings.php

TrustHosts middleware

This is used to get the host patterns that should be trusted. The path of this middleware is app/Http/Middleware/TrustHosts.php.

TrustProxies middleware

This is used to get the proxies that should be trusted for this application. The path of this is app/Http/Middleware/TrustProxies.php.

ValidateSignature middleware

This is used to validate incoming requests for the signed URL. The path of this is app/Http/Middleware/ValidateSignature.php.

VerifyCsrfToken middleware

This is used to protect our application from CSRF attacks by using CSRF tokens. The path of this is app/Http/Middleware/VerifyCsrfToken.php.

How to create middleware in Laravel?

Middleware can be created by executing the following artisan command:

php artisan make:middleware <MIDDLEWARE_NAME>

Here, MIDDLEWARE_NAME is to be replaced by the name of the middleware.

For a simple example, we will create a middleware here through which we will redirect to the inner page based on the role found in the URL and if there is a role other than certain roles, we will redirect to the 403 page.

Here the middleware has to be registered to use this middleware in the application, we will know that middleware can be registered in Laravel in different ways later in this article. Now in this example, we will assign middleware to route.

Create custom middleware (Role middleware)

We can create RoleMiddleware using the artisan command below:

php artisan make:middleware RoleMiddleware

This will create the RoleMiddleware.php in app/Http/Middleware.

We will now write a condition in the handle method of this file so that if the role found in the URL is not admin, editor, or author, then it will be redirected to the 403 page.

The code is as follows:

<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class RoleMiddleware
{    
    public function handle(Request $request, Closure $next): Response
    {
        if(!in_array($request->role, ['admin', 'editor', 'author'])) abort('403');
        return $next($request);
    }
}
Create a route and assign middleware

We will now write two web routes for the HTTP Get method in the routes\web.php file. For the page route, we will assign RoleMiddleware. The code is as follows.

<?php
use Illuminate\Support\Facades\Route;
use App\Http\Middleware\RoleMiddleware;

Route::get('/user', function () {
    return view('user');
})->name('user');

Route::get('/page/{role}', function () {
    return view('role');
})->middleware(RoleMiddleware::class)->name('page');
Create view files

We will create two view files – user.blade.php and role.balde.php in resources/views files.

The code of user.blade.php file is given below.

<strong>Users:</strong>
<br />
1 | Admin | <a href="{{ route('page', ['role'=> 'admin']) }}">Admin Interface</a>
<br />
2 | Editor | <a href="{{ route('page', ['role'=> 'editor']) }}">Editor Interface</a>
<br />
3 | Author | <a href="{{ route('page', ['role'=> 'author']) }}">Author Interface</a>

The code of role.blade.php file is given below.

Hi, {{ ucfirst(request()->route('role')) }}
Browser output

Now, if we browse http://<SITEURL>/user
then the browser output will look like the screenshot below :

Now on the above page if we click on any link of Admin Interface, Editor Interface, or Author Interface then we will be redirected to http://<SITEURL>/page/admin, http://<SITEURL>/page/editor, and http://<SITEURL>/page/author URL respectively.

The output of these pages will look like the screenshots below.

Screenshot of http://<SITEURL>/page/admin
Screenshot of http://<SITEURL>/page/editor
Screenshot of http://<SITEURL>/page/author

Now if the URL is http://<SITEURL>/page/subscriber then this URL will give 403 response. The screenshot is as follows.

Because I have added the following condition to the handle method of RoleMiddleware so that the page will give 403 response for any role other than admin, editor, or author.

if(!in_array($request->role, ['admin', 'editor', 'author'])) abort('403');

Now we will know when the middleware will perform the task.

When does middleware perform tasks?

The middleware can perform before or after the request goes deep into the application (like, goes to the next assigned middleware).

We can call them respectively before, or after middleware for our convenience. We can also combine before and after middleware and use it in our application and call it full for our convenience.

Before middleware.

In Before Middleware the request is passed ((like, passed to the next assigned middleware) after performing the task of the middleware. The syntax of the handle method of the middleware for this is as follows:

public function handle(Request $request, Closure $next): Response
{
    // Perform action
 
    return $next($request);
}

Here, return $next($request); is placed after some action in the handle method.

In the RoleMiddleware that we have created, the task of the middleware is performed first.

I will create another middleware by using the artisan command for better understanding.

php artisan make:middleware AnotherMiddleware

Here a new file called AnotherMiddleware.php will be created in this path app/Http/Middleware and I am making a small change in the handle method by adding an echo statement (echo "Another middleware | ";).

<?php
namespace App\Http\Middleware;

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

class AnotherMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        echo "Another middleware | ";
        return $next($request);
    }
}

We will also make a change in the handle method by adding an echo statement (echo "Performing task before | ";) in RoleMiddleware.php.

<?php

namespace App\Http\Middleware;

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

class RoleMiddleware
{    
    public function handle(Request $request, Closure $next): Response
    {
        // Perform action
        echo "Performing task before | ";	    
        if(!in_array($request->role, ['admin', 'editor', 'author'])) abort('403');
        return $next($request);
    }    
}

To understand better we will modify the page route also in routes/web.php. Here we have assigned AnotherMiddleware in addition to RoleMiddleware for the page route.

<?php
use Illuminate\Support\Facades\Route;
use App\Http\Middleware\RoleMiddleware;
use App\Http\Middleware\AnotherMiddleware;

Route::get('/user', function () {
    return view('user');
})->name('user');

Route::get('/page/{role}', function () {    
    return view('role');
})->middleware([RoleMiddleware::class, AnotherMiddleware::class])->name('page');

Now, if we browse http://<SITEURL>/page/admin the output of these pages will look like the screenshots below.

There are two middleware assigned in the page route, the first one is RoleMiddleware. This middleware performs the task first, and the ‘Performing task before |’ output is given. Then the request goes to the next assigned middleware i.e. AnotherMiddleware and outputs ‘Another middleware’. Finally ‘Hi, Admin’ is output from the view file.

After middleware

In After Middleware the request is passed ((like, passed to the next assigned middleware) before performing the task of the middleware. The syntax of the handle method of the middleware for this is as follows:

public function handle(Request $request, Closure $next): Response
{
    $response = $next($request);
 
    // Perform action
 
    return $response;
}

For better understanding, I will change RoleMiddleware by placing the echo statement after $response = $next($request);

<?php

namespace App\Http\Middleware;

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

class RoleMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);
        // Perform action
        echo "Performing task after | ";
        if(!in_array($request->role, ['admin', 'editor', 'author'])) abort('403');
        return $response;
    }
}

Now, if we browse http://<SITEURL>/page/admin the output of these pages will look like the screenshots below.

Here from the RoleMiddleware middleware, the request is passed to AnotherMiddleware and outputs ‘Another middleware’ then RoleMiddleware performs the task, and the ‘Performing task after |’ output is given and finally, the view file gives the ‘Hi, Admin’ output.

Full middleware

This is the combination of both Before Middleware and After Middleware. Here some tasks will be performed before going deep into the application and some tasks will be performed after going deep into the application. The syntax of the handle method of the middleware for this is as follows:

public function handle(Request $request, Closure $next): Response
{
    // Perform action    

    $response = $next($request);
 
    // Perform action
 
    return $response;
}

We will change our existing RoleMiddleware to better understand this type of middleware

<?php

namespace App\Http\Middleware;

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

class RoleMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        // Perform action
        echo "Performing task before | ";
	$response = $next($request);
        // Perform action
        echo "Performing task after | ";
        if(!in_array($request->role, ['admin', 'editor', 'author'])) abort('403');
        return $response;
    }
}

Now, if we browse http://<SITEURL>/page/admin the output of these pages will look like the screenshots below.

Here RoleMiddleware performs task first and ‘Performing task before |’ output is given. Then AnotherMiddleware outputs ‘Another middleware | ’. Then again RoleMiddleware performs the task and the ‘Performing task after |’ output is given. Finally, the view file gives the ‘Hi, Admin’ output.

How to register middleware in Laravel?

There are different ways to register middleware in Laravel. We will describe them one by one.

Assigning middleware globally

If we want to run a middleware for all HTTP requests, then we need to add the middleware class in the $middleware property of the app/Http/Kernel.php file.

For example, we want to run the AnotherMiddleware we created here for all HTTP requests, so we’ll add the AnotherMiddleware class to the $middleware property of the app/Http/Kernel.php file.

protected $middleware = [
        // \App\Http\Middleware\TrustHosts::class,
        \App\Http\Middleware\TrustProxies::class,
        \Illuminate\Http\Middleware\HandleCors::class,
        \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\AnotherMiddleware::class,
];

For user route in our routes/web.php, we have not directly assigned a middleware to the route.

But if we browse http://<SITEURL>/user then we will get output like following screenshot.

Here, since AnotherMiddleware is registered globally, AnotherMiddleware is run and the output of its echo statement is displayed.

If the middleware is registered globally like this, it will run for all HTTP requests i.e. web interface and API interface. That is, if we create a route in routes/api.php in the following way, then this middleware will also run.

<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::get('/userapi', function () {
    return 'Something';
});

The output for this api route i.e. http://<SITEURL>/api/userapi in POSTMAN will look like the screenshot below.

Here Another Middleware is run and for that, the echo statement gives the output ‘Another middleware’.

But the question is if we want to run any middleware only for the web or only for the api then what do we do?

The answer is that we can implement it through the middleware group.

Middleware group

In app\Http\Kernel.php we can find that there are predefined web and api middleware groups that contain some middleware to apply to our web and API routes.

It should be noted that the routes in the routes/web.php file define routes for the web interface and the routes in routes/api.php define routes for the API interface.

To understand this, we will comment \App\Http\Middleware\AnotherMiddleware::class, which we added in the $middleware property in the Kernel.php file earlier.

And if we want to run AnotherMiddleware only for all web routes then we add this middleware class in $middlewareGroups web property of app\Http\Kernel.php file.

protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
            \App\Http\Middleware\AnotherMiddleware::class,
        ],

        'api' => [
            // \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
];

if we browse http://<SITEURL>/user then we will get output like the following screenshot.

Here, since AnotherMiddleware is registered within the web group so for HTTP web routes, AnotherMiddleware is run and the output of its echo statement is displayed.

But now the output for this api route i.e. http://<SITEURL>/api/userapi in POSTMAN will look like the screenshot below.

This is because now AnotherMiddleware is no longer globally registered and AnotherMiddleware is not added for the api group so AnotherMiddleware is not run for this route.

In the same way, as we have added middleware for the web group, we could add middleware for the api group as well.

Assigning aliases to middleware

We can create aliases of middleware classes which will be convenient for use. For this, we can add a new alias in the middlewareAliases property in the app\Http\Kernel.php file. In this case, we will use the ‘role’ alias for RoleMiddleware and the ‘another’ alias for AnotherMiddleware.

protected $middlewareAliases = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'precognitive' => \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
        'signed' => \App\Http\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
        'role' => \App\Http\Middleware\RoleMiddleware::class,
        'another' => \App\Http\Middleware\AnotherMiddleware::class,
];

Now we will know how to assign middleware to route or controller.

Assigning middleware to routes

We have already learned from the example in this article how to assign one or more middleware to a route using the middleware method. We can assign single or multiple middleware to a route.

Assigning single middleware to routes

In this case, we will assign one middleware to a route. The example is given below.

Route::get('/page/{role}', function () {    
    return view('role');
})->middleware(RoleMiddleware::class)->name('page');

Or if we use aliases we can assign the middleware as follows.

Route::get('/page/{role}', function () {
    return view('role');
})->middleware('role')->name('page');
Assigning multiple middleware to routes

We can assign multiple middleware to a route by passing an array of middleware names to the middleware method. In this case, the priority of the middleware will be the one declared first in that array. The example is given below.

Route::get('/page/{role}', function () {    
    return view('role');
})->middleware([RoleMiddleware::class, AnotherMiddleware::class])->name('page');

Or if we use aliases we can assign the middleware as follows.

Route::get('/page/{role}', function () {    
    return view('role');
})->middleware(['role', 'another'])->name('page');

Now we will know how to exclude middleware in case of a route.

How to exclude middleware in Laravel?

If a middleware is assigned to a group but a particular route may not need to run that middleware, we will invoke the withoutMiddleware method on the route. The withoutMiddleware method does not apply to globally registered middleware.

For example, let’s assume we have globally registered AnotherMiddleware. I have written earlier in this article how to assign a middleware globally.

AnotherMiddleware middleware will run on all routes. But in case of any route, let’s say, user route, we don’t want AnotherMiddleware to run, in that case, the code will be as follows.

Route::get('/user', function () {
    return view('user');
})->withoutMiddleware([AnotherMiddleware::class])->name('user');

How to sort middleware?

If we need our middleware to execute in a specific order we can specify the priority using the $middlewarePriority property of app/Http/Kernel.php file.

protected $middlewarePriority = [
    \Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests::class,
    \Illuminate\Cookie\Middleware\EncryptCookies::class,
    \Illuminate\Session\Middleware\StartSession::class,
    \Illuminate\View\Middleware\ShareErrorsFromSession::class,
    \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class,
    \Illuminate\Routing\Middleware\ThrottleRequests::class,
    \Illuminate\Routing\Middleware\ThrottleRequestsWithRedis::class,
    \Illuminate\Contracts\Session\Middleware\AuthenticatesSessions::class,
    \Illuminate\Routing\Middleware\SubstituteBindings::class,
    \Illuminate\Auth\Middleware\Authorize::class,
];

Now we will learn how to pass parameters in middleware.

How to pass parameters in middleware?

Additional parameters can be passed to middleware and its syntax would be:

Route::get('/ROUTENAME', function () { })
->middleware('MIDDLEWARE:PARAMETR')
->name('ROUTENAME')

To understand this with our example we will change the page route.

Route::get('/page/{role}', function () {
    return view('role');
})->middleware('another:admin')->name('page');

Here we have passed the role (admin) to the middleware. We will also make some modifications to our existing AnotherMiddleware.

<?php

namespace App\Http\Middleware;

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

class AnotherMiddleware
{
    public function handle(Request $request, Closure $next, string $role): Response
    {
        echo "Role from request: ". $request->role. "<br>Role from parameter: ". $role. "<br>";
        return $next($request);
    }
}

Now if we browse http://<SITEURL>/page/admin the output will look like the screenshot below.

In the above code, we get the value of $request->role from the URL and the value of $role from the middleware parameter.

Now we will know what is Terminable Middleware.

What is terminable middleware?

If we want our middleware to do some work after the HTTP response has been sent to the browser. For this, we have to put a terminate method in the middleware.

The terminate method should have request and response parameters. Terminable middleware could be assigned directly to the route or globally in the app/Http/Kernel.php file.

We will try to understand this with examples. For this, we will add the terminate method to our existing AnotherMiddleware.

<?php

namespace App\Http\Middleware;

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

class AnotherMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        echo "Before HTTP response from route<br>";
        return $next($request);
    }

    public function terminate(Request $request, Response $response): void
    {
        echo "<br>After HTTP response from route";
    }
}

For this I will create a route called terminate in routes\web.php and return a response from this route.

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Middleware\AnotherMiddleware;

Route::get('/terminate', function () {
    return 'HTTP response from route';
})->middleware([AnotherMiddleware::class])->name('terminate');

Now, if we browse http://<SITEURL>/terminate, we will get the following output.

Here first the output of the echo statement of the handle method of the middleware is displayed. Then the response of the route is displayed and finally, the output of the echo statement of the terminate method is displayed.

I have discussed about middleware in Laravel here in detail. You can learn more at: https://laravel.com/docs/10.x/middleware.