{"id":180,"date":"2025-08-26T14:46:25","date_gmt":"2025-08-26T14:46:25","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=180"},"modified":"2025-08-26T14:46:28","modified_gmt":"2025-08-26T14:46:28","slug":"how-to-restrict-page-access-by-role-in-laravel-12","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/how-to-restrict-page-access-by-role-in-laravel-12\/","title":{"rendered":"How to Restrict Page Access by Role in Laravel 12"},"content":{"rendered":"\n<p>When building modern web applications, controlling <strong>who can see what<\/strong> is just as important as authentication. Not all users should have the same level of access \u2014 for example, an admin may need to manage users, while regular users should only see their own content. This is where <strong>Role-Based Access Control (RBAC)<\/strong> comes in.<\/p>\n\n\n\n<p>In this tutorial, you\u2019ll learn <strong>how to restrict page access by role in Laravel 12<\/strong>. We\u2019ll cover what roles and permissions are, how to create a simple role system, how to assign roles to users, and how to protect routes and pages so that only authorized users can see them.<\/p>\n\n\n\n<p><\/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; What are Roles and Permissions?<\/strong><\/h2>\n\n\n\n<p><strong>Role:<\/strong> A role represents a group of permissions that can be assigned to a user. Examples include <code>admin<\/code>, <code>editor<\/code>, or <code>user<\/code>.<\/p>\n\n\n\n<p><strong>Permission:<\/strong> A permission defines what an account is allowed to do, such as <code>edit-post<\/code> or <code>delete-user<\/code>. Roles usually contain multiple permissions.<\/p>\n\n\n\n<p>By combining them, you can control what different users can access in your Laravel 12 app. In this guide, we\u2019ll implement a basic version without external packages, so you understand the inner workings.<\/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; Prerequisites<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A fresh <strong>Laravel 12<\/strong> project (<a href=\"\/blog\/setting-up-fresh-laravel-12-project\">see our setup guide<\/a>)<\/li>\n<li>Basic authentication already working (<a href=\"\/blog\/how-to-add-authentication-in-laravel-12-without-fortify\">see our Laravel auth guide<\/a>)<\/li>\n<li>A database with the <code>users<\/code> table ready<\/li>\n<\/ul>\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; Update the Users Table with a Role Column<\/strong><\/h2>\n\n\n\n<p>We\u2019ll keep things simple by adding a <code>role<\/code> column directly to the <code>users<\/code> table. Later, you can expand this into a full permissions table if needed.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">php artisan make:migration add_role_to_users_table --table=users<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\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\">\/\/ database\/migrations\/xxxx_xx_xx_xxxxxx_add_role_to_users_table.php<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Migrations<\/span>\\<span class=\"hljs-title\">Migration<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Database<\/span>\\<span class=\"hljs-title\">Schema<\/span>\\<span class=\"hljs-title\">Blueprint<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Schema<\/span>;\n\n<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Migration<\/span> <\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">up<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span> <\/span>{\n        Schema::table(<span class=\"hljs-string\">'users'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Blueprint $table)<\/span> <\/span>{\n            $table-&gt;string(<span class=\"hljs-string\">'role'<\/span>)-&gt;default(<span class=\"hljs-string\">'user'<\/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\">down<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span> <\/span>{\n        Schema::table(<span class=\"hljs-string\">'users'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Blueprint $table)<\/span> <\/span>{\n            $table-&gt;dropColumn(<span class=\"hljs-string\">'role'<\/span>);\n        });\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>This migration adds a new <code>role<\/code> field with a default value of <code>user<\/code>. That means anyone who registers gets the \u201cuser\u201d role automatically, unless you assign them another one (like \u201cadmin\u201d).<\/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; Middleware for Role Protection<\/strong><\/h2>\n\n\n\n<p>To restrict access, we\u2019ll create a custom middleware that checks if the current user has the required role.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">php artisan make:middleware RoleMiddleware<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/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\/RoleMiddleware.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\">Support<\/span>\\<span class=\"hljs-title\">Facades<\/span>\\<span class=\"hljs-title\">Auth<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">RoleMiddleware<\/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, Closure $next, $role)<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">if<\/span> (!Auth::check() || Auth::user()-&gt;role !== $role) {\n            abort(<span class=\"hljs-number\">403<\/span>, <span class=\"hljs-string\">'Unauthorized'<\/span>);\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>This middleware compares the user\u2019s <code>role<\/code> column with the role required. If it doesn\u2019t match, we abort with a 403 (Forbidden).<\/p>\n\n\n\n<p>Now register the middleware in <code>app\/Http\/Kernel.php<\/code>:<\/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\"><span class=\"hljs-comment\">\/\/ app\/Http\/Kernel.php<\/span>\n<span class=\"hljs-keyword\">protected<\/span> $routeMiddleware = &#91;\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    <span class=\"hljs-string\">'role'<\/span> =&gt; \\App\\Http\\Middleware\\RoleMiddleware::class,\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<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>5 &#8211; Protecting Routes by Role<\/strong><\/h2>\n\n\n\n<p>Now that the middleware is registered, apply it to routes. For example, let\u2019s protect an admin dashboard so only users with the <code>admin<\/code> role can access it.<\/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\">\/\/ routes\/web.php<\/span>\nRoute::get(<span class=\"hljs-string\">'\/admin'<\/span>, <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">()<\/span> <\/span>{\n    <span class=\"hljs-keyword\">return<\/span> view(<span class=\"hljs-string\">'admin.dashboard'<\/span>);\n})-&gt;middleware(&#91;<span class=\"hljs-string\">'auth'<\/span>,<span class=\"hljs-string\">'verified'<\/span>,<span class=\"hljs-string\">'role:admin'<\/span>]);<\/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>Here, we stacked three middleware: <code>auth<\/code> (user must be logged in), <code>verified<\/code> (user must have confirmed email), and <code>role:admin<\/code> (user must be an admin). Any unauthorized user will see a 403 error automatically.<\/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; Adding Role Selection in the UI<\/strong><\/h2>\n\n\n\n<p>If you want to let an admin assign roles via the UI, add a simple form. For example, let\u2019s create a form to update a user\u2019s role:<\/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\">&lt;!-- resources\/views\/admin\/edit-user.blade.php --&gt;\n@extends(<span class=\"hljs-string\">'layouts.app'<\/span>)\n\n@section(<span class=\"hljs-string\">'content'<\/span>)\n&lt;div <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span>=\"<span class=\"hljs-title\">container<\/span>\"&gt;\n  &lt;<span class=\"hljs-title\">h2<\/span>&gt;<span class=\"hljs-title\">Edit<\/span> <span class=\"hljs-title\">User<\/span> <span class=\"hljs-title\">Role<\/span>&lt;\/<span class=\"hljs-title\">h2<\/span>&gt;\n  &lt;<span class=\"hljs-title\">form<\/span> <span class=\"hljs-title\">method<\/span>=\"<span class=\"hljs-title\">POST<\/span>\" <span class=\"hljs-title\">action<\/span>=\"<\/span>{{ route(<span class=\"hljs-string\">'admin.users.update'<\/span>, $user) }}<span class=\"hljs-string\">\"&gt;\n    @csrf\n    @method('PUT')\n    &lt;div class=\"<\/span>mb<span class=\"hljs-number\">-3<\/span><span class=\"hljs-string\">\"&gt;\n      &lt;label class=\"<\/span>form-label<span class=\"hljs-string\">\"&gt;Role&lt;\/label&gt;\n      &lt;select name=\"<\/span>role<span class=\"hljs-string\">\" class=\"<\/span>form-control<span class=\"hljs-string\">\"&gt;\n        &lt;option value=\"<\/span>user<span class=\"hljs-string\">\" {{ $user-&gt;role==='user'?'selected':'' }}&gt;User&lt;\/option&gt;\n        &lt;option value=\"<\/span>admin<span class=\"hljs-string\">\" {{ $user-&gt;role==='admin'?'selected':'' }}&gt;Admin&lt;\/option&gt;\n      &lt;\/select&gt;\n    &lt;\/div&gt;\n    &lt;button class=\"<\/span>btn btn-primary<span class=\"hljs-string\">\"&gt;Update Role&lt;\/button&gt;\n  &lt;\/form&gt;\n&lt;\/div&gt;\n@endsection<\/span><\/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>This gives administrators a dropdown to assign <code>user<\/code> or <code>admin<\/code>. You can extend this idea with a dedicated <code>roles<\/code> table for more complex scenarios.<\/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>7 &#8211; Common Errors &amp; Fixes<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>403 error even for admins:<\/strong> Ensure the <code>role<\/code> column has the correct value and your middleware is spelled correctly.<\/li>\n<li><strong>Middleware not working:<\/strong> Double-check that you registered the middleware alias in <code>Kernel.php<\/code>.<\/li>\n<li><strong>New users can\u2019t access pages:<\/strong> Remember that the default <code>role<\/code> is <code>user<\/code>. If you want new admins, set their role after registration.<\/li>\n<\/ul>\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>Wrapping Up<\/strong><\/h2>\n\n\n\n<p>You\u2019ve successfully restricted pages by role in Laravel 12. We started with a simple role column, created a custom <code>RoleMiddleware<\/code>, and applied it to routes. We also built a small UI for admins to change user roles. This approach is simple and great for beginners; as your app grows, you might switch to a package like Spatie Permissions for more advanced control.<\/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>What\u2019s Next<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/laravel-spatie-permissions-step-by-step-installation-setup\">Laravel Spatie Permissions: Step-by-Step Installation &amp; Setup<\/a> \u2014 learn the professional way of handling roles.<\/li>\n<li><a href=\"\/blog\/creating-a-user-friendly-roles-permissions-ui-in-laravel\">Creating a User-Friendly Roles &amp; Permissions UI in Laravel<\/a> \u2014 manage roles and permissions directly in the dashboard.<\/li>\n<li><a href=\"\/blog\/how-to-give-and-revoke-permissions-to-users-in-laravel\">How to Give and Revoke Permissions to Users in Laravel<\/a> \u2014 control access dynamically.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>When building modern web applications, controlling who can see what is just as important as authentication. Not all users should have the same level of access \u2014 for example, an admin may need to manage users, while regular users should only see their own content. This is where Role-Based Access Control (RBAC) comes in. In [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":184,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[23,16,15,17],"class_list":["post-180","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\/180","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=180"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/180\/revisions"}],"predecessor-version":[{"id":183,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/180\/revisions\/183"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/184"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=180"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=180"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=180"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}