{"id":251,"date":"2025-08-27T16:18:59","date_gmt":"2025-08-27T16:18:59","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=251"},"modified":"2025-08-27T16:19:01","modified_gmt":"2025-08-27T16:19:01","slug":"laravel-middleware-for-role-based-route-protection","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/laravel-middleware-for-role-based-route-protection\/","title":{"rendered":"Laravel Middleware for Role-Based Route Protection"},"content":{"rendered":"\n<p>In every web application, certain routes should only be accessible by specific users. For example, an <strong>admin dashboard<\/strong> should be restricted to admins, while editors may only manage content. In <strong>Laravel 12<\/strong>, this is best achieved using <strong>middleware<\/strong> \u2014 small classes that filter requests before they reach controllers.<\/p>\n\n\n\n<p>In this guide, you\u2019ll learn how to use middleware to <strong>protect routes by role and permission<\/strong>. We\u2019ll cover Spatie\u2019s built-in middleware, how to create your own custom middleware, and how to apply them effectively in your application.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>1 &#8211; Why Middleware for Roles?<\/strong><\/h2>\n\n\n\n<p>Middleware acts like a security checkpoint. When a request enters your app, middleware decides if it should continue. With <strong>role-based middleware<\/strong>, you can make sure that only users with the right roles\/permissions can access sensitive pages.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>2 &#8211; Spatie\u2019s Role &amp; Permission Middleware<\/strong><\/h2>\n\n\n\n<p>When you install <a href=\"\/blog\/laravel-spatie-permissions-step-by-step-installation-setup\">Spatie Permissions<\/a>, it registers two middleware out of the box:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>role:&lt;role><\/code> \u2014 restricts access to a role<\/li>\n\n\n\n<li><code>permission:&lt;permission><\/code> \u2014 restricts access to a permission<\/li>\n<\/ul>\n\n\n\n<p>Example usage:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ routes\/web.php<\/span>\n\nRoute::middleware(&#91;<span class=\"hljs-string\">'auth'<\/span>,<span class=\"hljs-string\">'role:admin'<\/span>])-&gt;group(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n    Route::get(<span class=\"hljs-string\">'\/admin\/dashboard'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">'Welcome Admin'<\/span>;\n    });\n});\n\nRoute::middleware(&#91;<span class=\"hljs-string\">'auth'<\/span>,<span class=\"hljs-string\">'permission:publish posts'<\/span>])-&gt;group(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n    Route::get(<span class=\"hljs-string\">'\/editor\/posts'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">'Editor Post Management'<\/span>;\n    });\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now only admins can access <code>\/admin\/dashboard<\/code>, and only users with the <code>publish posts<\/code> permission can access <code>\/editor\/posts<\/code>.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>3 &#8211; Multiple Roles &amp; Permissions<\/strong><\/h2>\n\n\n\n<p>You can also allow multiple roles or permissions in a single middleware declaration by separating them with a pipe (<code>|<\/code>):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ routes\/web.php<\/span>\n\nRoute::middleware(&#91;<span class=\"hljs-string\">'auth'<\/span>,<span class=\"hljs-string\">'role:admin|manager'<\/span>])-&gt;group(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n    Route::get(<span class=\"hljs-string\">'\/reports'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">'Reports Page'<\/span>;\n    });\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In this example, both <code>admin<\/code> and <code>manager<\/code> roles can access the <code>\/reports<\/code> route.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>4 &#8211; Creating Custom Middleware<\/strong><\/h2>\n\n\n\n<p>Sometimes, Spatie\u2019s built-in middleware isn\u2019t enough. For example, maybe you want to restrict access based on <strong>both a role and a specific condition<\/strong>. In that case, you can create your own middleware.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">php<\/span> <span class=\"hljs-selector-tag\">artisan<\/span> <span class=\"hljs-selector-tag\">make<\/span><span class=\"hljs-selector-pseudo\">:middleware<\/span> <span class=\"hljs-selector-tag\">CheckEditorApproval<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Middleware\/CheckEditorApproval.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Middleware<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Closure<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Request<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">CheckEditorApproval<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handle<\/span><span class=\"hljs-params\">(Request $request, Closure $next)<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">if<\/span> (! $request-&gt;user()-&gt;hasRole(<span class=\"hljs-string\">'editor'<\/span>) || ! $request-&gt;user()-&gt;approved) {\n            abort(<span class=\"hljs-number\">403<\/span>, <span class=\"hljs-string\">'Access denied.'<\/span>);\n        }\n\n        <span class=\"hljs-keyword\">return<\/span> $next($request);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Register your middleware in <code>app\/Http\/Kernel.php<\/code> under <code>$routeMiddleware<\/code> and apply it like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">Route::middleware(&#91;<span class=\"hljs-string\">'auth'<\/span>,<span class=\"hljs-string\">'check.editor'<\/span>])-&gt;group(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n    Route::get(<span class=\"hljs-string\">'\/editor\/dashboard'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">'Editor Dashboard'<\/span>;\n    });\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This ensures that only approved editors can access the dashboard.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>5 &#8211; Applying Middleware in Controllers<\/strong><\/h2>\n\n\n\n<p>You don\u2019t have to declare middleware only in routes. You can also apply them directly inside controllers:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/Admin\/DashboardController.php<\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">DashboardController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Controller<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">__construct<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;middleware(&#91;<span class=\"hljs-string\">'auth'<\/span>,<span class=\"hljs-string\">'role:admin'<\/span>]);\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'admin.dashboard'<\/span>);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This way, any route pointing to <code>DashboardController<\/code> will automatically be protected by the <code>admin<\/code> role requirement.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>6 &#8211; Using HasMiddleware in Laravel 12<\/strong><\/h2>\n\n\n\n<p>Laravel 12 introduces a modern way to assign middleware directly in controllers using the <code>HasMiddleware<\/code> interface and a static <code>middleware()<\/code> method. This is cleaner than the old <code>$this-&gt;middleware()<\/code> approach and doesn\u2019t require constructors.<\/p>\n\n\n\n<p>\u26a0\ufe0f Important: In older Laravel versions, your base controller extended <code>Illuminate\\Routing\\Controller<\/code> (aliased as <code>BaseController<\/code>). That class contains an instance <code>middleware()<\/code> method, which <strong>conflicts<\/strong> with the new static <code>middleware()<\/code> method. In Laravel 12, controllers no longer need to extend <code>Illuminate\\Routing\\Controller<\/code>. You should update your <code>App\\Http\\Controllers\\Controller<\/code> to remove the inheritance.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ Before (default older style)<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Routing<\/span>\\<span class=\"hljs-title\">Controller<\/span> <span class=\"hljs-title\">as<\/span> <span class=\"hljs-title\">BaseController<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Controller<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">BaseController<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">AuthorizesRequests<\/span>, <span class=\"hljs-title\">ValidatesRequests<\/span>;\n}\n\n<span class=\"hljs-comment\">\/\/ After (Laravel 12 style \u2014 no BaseController needed)<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Controller<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">AuthorizesRequests<\/span>, <span class=\"hljs-title\">ValidatesRequests<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now, you can safely implement <code>HasMiddleware<\/code> in your controllers without conflicts:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ app\/Http\/Controllers\/Admin\/DashboardController.php<\/span>\n\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>\\<span class=\"hljs-title\">Admin<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Http<\/span>\\<span class=\"hljs-title\">Controllers<\/span>\\<span class=\"hljs-title\">Controller<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Routing<\/span>\\<span class=\"hljs-title\">Controllers<\/span>\\<span class=\"hljs-title\">HasMiddleware<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Routing<\/span>\\<span class=\"hljs-title\">Controllers<\/span>\\<span class=\"hljs-title\">Middleware<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">DashboardController<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Controller<\/span> <span class=\"hljs-keyword\">implements<\/span> <span class=\"hljs-title\">HasMiddleware<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">middleware<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">array<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> &#91;\n            <span class=\"hljs-keyword\">new<\/span> Middleware(<span class=\"hljs-string\">'auth'<\/span>),\n            <span class=\"hljs-keyword\">new<\/span> Middleware(<span class=\"hljs-string\">'role:admin'<\/span>),\n        ];\n    }\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">index<\/span><span class=\"hljs-params\">()<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'admin.dashboard'<\/span>);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>With this setup, all actions in the <code>DashboardController<\/code> are automatically protected by the <code>auth<\/code> and <code>role:admin<\/code> middleware. You can also restrict specific actions using <code>only<\/code> or <code>except<\/code> options.<\/p>\n\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>Middleware is the most effective way to enforce role-based security in Laravel 12. With Spatie\u2019s built-in middleware, you can restrict routes by role or permission in seconds. For advanced scenarios, you can write custom middleware to check additional conditions. By combining these approaches, you can secure every part of your app\u2019s routes with precision.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">What\u2019s Next<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/building-a-role-based-admin-panel-in-laravel-12\">Building a Role-Based Admin Panel in Laravel 12<\/a> \u2014 complete role &amp; permission management.<\/li>\n\n\n\n<li><a href=\"\/blog\/creating-a-role-specific-dashboard-in-laravel-12\">Creating a Role-Specific Dashboard in Laravel 12<\/a> \u2014 tailor dashboards by role.<\/li>\n\n\n\n<li><a href=\"\/blog\/how-to-create-a-multi-level-role-and-permission-system-in-laravel\">How to Create a Multi-Level Role &amp; Permission System in Laravel<\/a> \u2014 learn advanced role hierarchies.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>In every web application, certain routes should only be accessible by specific users. For example, an admin dashboard should be restricted to admins, while editors may only manage content. In Laravel 12, this is best achieved using middleware \u2014 small classes that filter requests before they reach controllers. In this guide, you\u2019ll learn how to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":255,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[23,16,15,17],"class_list":["post-251","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-access-control","tag-permissions","tag-roles","tag-roles-and-permissions"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/251","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/comments?post=251"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/251\/revisions"}],"predecessor-version":[{"id":254,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/251\/revisions\/254"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/255"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=251"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=251"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=251"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}