{"id":185,"date":"2025-08-26T14:52:57","date_gmt":"2025-08-26T14:52:57","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=185"},"modified":"2025-08-26T14:52:59","modified_gmt":"2025-08-26T14:52:59","slug":"how-to-expire-user-sessions-automatically-in-laravel","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/how-to-expire-user-sessions-automatically-in-laravel\/","title":{"rendered":"How to Expire User Sessions Automatically in Laravel"},"content":{"rendered":"\n<p>When building secure applications, controlling <strong>how long a user session stays active<\/strong> is critical. If sessions never expire, users might remain logged in for days, weeks, or even indefinitely \u2014 which increases the risk of stolen sessions, unauthorized access, or compliance issues in industries like finance or healthcare.<\/p>\n\n\n\n<p>In this guide, you\u2019ll learn <strong>how to expire user sessions automatically in Laravel 12<\/strong>. We\u2019ll go step by step through Laravel\u2019s built-in session lifetime configuration, how to implement absolute session timeouts with middleware, and even how to manage session expiration at the database level. Along the way, we\u2019ll also cover common mistakes and how to fix 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; Understanding Laravel Sessions<\/strong><\/h2>\n\n\n\n<p>Laravel uses <strong>sessions<\/strong> to keep track of logged-in users across multiple requests. By default, session data is stored in files (<code>storage\/framework\/sessions<\/code>), but you can also use databases, Redis, or Memcached. Each session has an <code>expiration time<\/code>, which defines how long the user can stay logged in without activity.<\/p>\n\n\n\n<p>There are two types of expiration you may want to configure:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Idle timeout (sliding expiration):<\/strong> The session ends after X minutes of inactivity. Each request resets the timer.<\/li>\n<li><strong>Absolute timeout:<\/strong> The session ends after X minutes, no matter how active the user is.<\/li>\n<\/ul>\n\n\n\n<p>Laravel provides idle timeout out of the box via <code>SESSION_LIFETIME<\/code>. For absolute timeout, we\u2019ll implement custom middleware.<\/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; Configure Idle Timeout in <code>.env<\/code><\/strong><\/h2>\n\n\n\n<p>By default, sessions last 120 minutes (2 hours). To reduce this to 30 minutes of inactivity, set the following in your <code>.env<\/code> file:<\/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\"><span class=\"hljs-comment\"># .env<\/span>\nSESSION_LIFETIME=30<\/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\n<p>This means if a user is inactive for 30 minutes, their session will automatically expire. Once they try to interact with the app again, they\u2019ll be redirected to the login page.<\/p>\n\n\n\n<p>Don\u2019t forget to clear and cache your configuration after changing the <code>.env<\/code> file:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">php artisan config:clear\nphp artisan config:cache<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><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\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; Implement Absolute Session Timeout<\/strong><\/h2>\n\n\n\n<p>Some applications require sessions to expire after a fixed duration, even if the user is actively browsing. For example, a banking app may force a logout after 1 hour for compliance reasons. Laravel doesn\u2019t provide this out of the box, but you can implement it with custom middleware.<\/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 SessionTimeout<\/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\/SessionTimeout.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\">SessionTimeout<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">protected<\/span> $timeout = <span class=\"hljs-number\">3600<\/span>; <span class=\"hljs-comment\">\/\/ 1 hour (in seconds)<\/span>\n\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)<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">if<\/span> (Auth::check()) {\n            $lastActivity = session(<span class=\"hljs-string\">'last_activity_time'<\/span>);\n            $currentTime = time();\n\n            <span class=\"hljs-keyword\">if<\/span> ($lastActivity &amp;&amp; ($currentTime - $lastActivity) &gt; <span class=\"hljs-keyword\">$this<\/span>-&gt;timeout) {\n                Auth::logout();\n                $request-&gt;session()-&gt;invalidate();\n                $request-&gt;session()-&gt;regenerateToken();\n\n                <span class=\"hljs-keyword\">return<\/span> redirect()-&gt;route(<span class=\"hljs-string\">'login'<\/span>)\n                    -&gt;withErrors(&#91;<span class=\"hljs-string\">'email'<\/span> =&gt; <span class=\"hljs-string\">'Your session has expired. Please log in again.'<\/span>]);\n            }\n\n            session(&#91;<span class=\"hljs-string\">'last_activity_time'<\/span> =&gt; $currentTime]);\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>This middleware tracks the last activity timestamp and forces logout if the session has exceeded one hour. You can adjust the <code>$timeout<\/code> value as needed.<\/p>\n\n\n\n<p>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> $middlewareGroups = &#91;\n    <span class=\"hljs-string\">'web'<\/span> =&gt; &#91;\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n        \\App\\Http\\Middleware\\SessionTimeout::class,\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>Now every request will check if the session has exceeded the absolute timeout limit. If so, the user is logged out and redirected to the login page.<\/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; Store Sessions in the Database (Optional)<\/strong><\/h2>\n\n\n\n<p>By default, Laravel stores sessions in files. If you want more control \u2014 like forcing logouts across devices \u2014 switch to the <code>database<\/code> driver. This way, you can manage sessions with SQL queries.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">php artisan session:table\nphp artisan migrate<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><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\n<p>This creates a <code>sessions<\/code> table. Now update <code>.env<\/code> to use it:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\"><span class=\"hljs-comment\"># .env<\/span>\nSESSION_DRIVER=database<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><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\n<p>Advantages of database sessions:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>You can see who is logged in by querying the <code>sessions<\/code> table.<\/li>\n<li>You can force logouts by deleting rows.<\/li>\n<li>You can implement additional expiration rules at the database level.<\/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>5 &#8211; Auto-Expire Sessions on Browser Close<\/strong><\/h2>\n\n\n\n<p>If you want sessions to expire immediately when the browser closes, set the <code>expire_on_close<\/code> option in <code>config\/session.php<\/code> to <code>true<\/code>:<\/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\">\/\/ config\/session.php<\/span>\n<span class=\"hljs-string\">'expire_on_close'<\/span> =&gt; <span class=\"hljs-keyword\">true<\/span>,<\/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 enabled, the session cookie is deleted when the browser closes, requiring the user to log in again next time they open the site.<\/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; Common Errors &amp; Fixes<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u201cSession expired too quickly\u201d:<\/strong> Check <code>SESSION_LIFETIME<\/code>. It counts minutes of inactivity, not absolute time.<\/li>\n<li><strong>Users not logged out after timeout:<\/strong> Make sure your custom middleware is registered in <code>Kernel.php<\/code> and applied to the <code>web<\/code> group.<\/li>\n<li><strong>Session not persisting after login:<\/strong> Ensure <code>APP_URL<\/code> and <code>SESSION_DOMAIN<\/code> are set correctly in <code>.env<\/code>, especially if using subdomains.<\/li>\n<li><strong>Database sessions not working:<\/strong> Confirm the <code>sessions<\/code> table exists and you\u2019ve set <code>SESSION_DRIVER=database<\/code>.<\/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\">Wrapping Up<\/h2>\n\n\n\n<p>You now know multiple ways to <strong>automatically expire user sessions in Laravel 12<\/strong>. You can configure idle timeouts with <code>SESSION_LIFETIME<\/code>, implement absolute timeouts with middleware, or even store sessions in the database for maximum control. Combined with features like email verification and two-factor authentication, this helps you build a secure, professional-grade Laravel 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\">What\u2019s Next<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/implementing-two-factor-authentication-in-laravel\">Implementing Two-Factor Authentication in Laravel<\/a> \u2014 add another layer of login security.<\/li>\n<li><a href=\"\/blog\/how-to-prevent-csrf-xss-and-sql-injection-in-laravel-apps\">How to Prevent CSRF, XSS, and SQL Injection in Laravel Apps<\/a> \u2014 protect against common vulnerabilities.<\/li>\n<li><a href=\"\/blog\/laravel-authentication-redirect-after-login-logout\">Laravel Authentication: Redirect Users After Login &amp; Logout<\/a> \u2014 improve user experience.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n","protected":false},"excerpt":{"rendered":"<p>When building secure applications, controlling how long a user session stays active is critical. If sessions never expire, users might remain logged in for days, weeks, or even indefinitely \u2014 which increases the risk of stolen sessions, unauthorized access, or compliance issues in industries like finance or healthcare. In this guide, you\u2019ll learn how to [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":189,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[22,24],"class_list":["post-185","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-security","tag-sessions"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/185","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=185"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/185\/revisions"}],"predecessor-version":[{"id":188,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/185\/revisions\/188"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/189"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}