Session lifetime is not being honored. Sessions are randomly expiring.
See forum post for more details:
http://forums.laravel.io/viewtopic.php?id=10839
Our configuration is:
1) Custom authentication driver
2) app/config/session.php => 'driver' => 'database'
3) app/config/session.php => 'lifetime' => 0
4) app/config/session.php => 'lottery' => array( 2, 100 )
We have tried setting the lifetime to 999 and our sessions still randomly expire.
We are on our own dedicated web server (Ubuntu + Apache + php5).
Laravel 4.0.x
Can you verify that cookies are not the problem? E.g. verify the expiration date of the session cookie.
Looking at the cookie and it says expires at end of session:
Name: laravel_session
Content: ug39g6vvjjln7li9i2g1sn0fk4
Host: (myhost)
Path: /
Send for: Any type of connection
Expires: At end of session
OK, from what we can tell is that every time the session -> lottery runs it expires ALL sessions regardless of their age.
That's odd. The database driver is actually maintained by Symfony since it's their PDO session handler.
If you're seeing this behavior with the database session driver I would raise an issue on the Symfony\HttpFoundation PDOSessionHandler because that is what handles the deletion of old sessions, etc.
OK, so I traced it down.
The issue appears that the wrong session option is being used either by Symfony or Laravel.
When I trace through it, I find that the 'gc_maxlifetime' is set to 1440 in NativeSessionStroage.php.
When Laravel sets the options, it passes 'cookie_lifetime' as the value set in app/config/session.php.
However, when the gc (garbage collection) fires off in symfony SessionHandler, it calls PdoSessionHandler.php -> gc() using the $maxlifetime value (1440). It then deletes ALL the sessions from the table over 1440.
I don't see the logic working properly here at all. It does NOT matter what you set LIFETIME too, 1440 is absolute according to this logic.
Here is my call Stack trace:
vendor/symfony/http-foundation/Symfony/Component/HttpFoundation/Session/Storage/Handler
PdoSessionHandler.php.Symfony\Component\HttpFoundationSessionStorage\Handler PdoSessionHandler->gc:107
bootstrap/compiled.php.Symfony\Component\HttpFoundationSessionStorage\ProxySessionHandlerProxy->gc:2768
bootstrap/compiled.php.session_start:2206
bootstrap/compiled.php.Symfony\Component\HttpFoundationSessionStorage\NativeSessionStorage->start:2206
bootstrap/compiled.php.Symfony\Component\HttpFoundationSessionSession->start:2072
bootstrap/compiled.php.IlluminateSessionStore->start:6534
bootstrap/compiled.php.IlluminateSessionSessionServiceProvider->IlluminateSession{closure}:4360
bootstrap/compiled.php.call_user_func:528
bootstrap/compiled.php.Illuminate\Foundation\Application->fireAppCallbacks:528
bootstrap/compiled.php.Illuminate\Foundation\Application->boot:513
vendor/laravel/framework/src/Illuminate/Foundation/start.php.require:208
bootstrap/start.php.require_once:59
public/index.php.{main}:35
Same problem here. "Native Sessions" can expire randomly, even session lifetime is set to 0 which is end of the browser session.
Ok, I think I've found the problem, and it appears a small Laravel issue, or misunderstanding.
When Illuminate/Session/SessionManager.php->buildSession() is called, it is passed the values as in Illuminate/Session/SessionManager.php->getOptions().
Here we see that the app.lifetime option in fact refers to the cookie expiration time (session.cookie_lifetime), while the session.gc_maxlifetime directive stays the default, ie. 1440s.
See this page for a more in-depth explanation of the two.
I would suggest to modify the app.php config file and allow users to set the two options separately, then in getOptions() set session.gc_maxlifetime as well. Waiting for a fix, I'm currently looking for a way to add this behavior without modifying the framework files. Will keep this post updated.
Manuele
ps: thanks to @vstokesjr for pointing out the direction
Copying over the post from http://forums.laravel.io/viewtopic.php?pid=58777#p58777.
Note that I'm extending Laravel as described in the documentation (http://laravel.com/docs/extending#ioc-based-extension).
First we add an option to config/app.php as the following (I've set it to 24 hours):
/*
|--------------------------------------------------------------------------
| Session Files Lifetime
|--------------------------------------------------------------------------
|
| Here you may specify the number of minutes after which old sessions in
| storage will be seen as 'garbage' and cleaned up. Garbage collection may
| occur during session start, depending on the lottery. The default is 24.
|
*/
'files_lifetime' => 24*60,
We then create two files. In SessionServiceProvider.php we overwrite the SessionManager register to use our own SessionManager:
<?php namespace YourNamespace\Session;
class SessionServiceProvider extends \Illuminate\Session\SessionServiceProvider {
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->setupDefaultDriver();
$this->registerSessionManager();
$this->registerSessionDriver();
}
/**
* Register the session manager instance.
*
* @return void
*/
protected function registerSessionManager()
{
$this->app['session.manager'] = $this->app->share(function($app)
{
return new SessionManager($app);
});
}
}
In SessionManager.php we overwrite the getOptions() method to set gc_maxlifetime as well:
<?php namespace YourNamespace\Session;
class SessionManager extends \Illuminate\Session\SessionManager {
/**
* Get the session options.
*
* @return array
*/
protected function getOptions()
{
$config = $this->app['config']['session'];
return array(
'cookie_domain' => $config['domain'], 'cookie_lifetime' => $config['lifetime'] * 60,
'cookie_path' => $config['path'], 'cookie_httponly' => '1', 'name' => $config['cookie'],
'gc_divisor' => $config['lottery'][1], 'gc_probability' => $config['lottery'][0],
'gc_maxlifetime' => $config['files_lifetime'] * 60,
);
}
}
If you set 'files_lifetime' => 24_60, and then in your SessionManager class use $config['files_lifetime'] * 60, aren't you just taking 24 minutes and turning it into 24_60 minutes?
24 * 60 * 60 is 86.400, that is the number of seconds in a day. I'm setting my sessions to last at least one day (that is my specific case of course).
Understood. Yeah, I actually updated my php.ini to reflect 86400 (24 hours) this morning after reading this issue, and neither my client NOR myself has experienced a single "random" logout. Cheers!
That will work as well :)
OK. What do we do now as an end-user? Is it enough to update Laravel via Composer in order to solve the problem?
Nope. @taylorotwell will decide if he wants to include this feature or not. If you want to have it configurable via Laravel's config files, follow my indications (you will have to make those files yourself). If you want to do it quick and simple, just update your php.ini (and your server's).
It's been 4 days, and nobody (of ~12,000 users) has reported a single random logout after updating my session.gc_maxlifetime. :+1:
So why do we need a new "files_liftetime" option instead of just using the regular "lifetime" option?
Because if I set "lifetime" to 0, how does "files_lifetime" behave then? Of course one option only to set would be better, session.gc_maxlifetime could be set to 365 days or something...?
I'm not honestly sure the framework needs to be updated? Unless I'm just missing the point (maybe I am) -- it sort of falls under a "server configuration" issue, not necessarily a code issue? Though I suppose if it's easy to implement, there's no harm there.
"lifetime" is a server configuration as well (session.cookie_lifetime), but we have a convenient way to set it in the config files. I liked that, that's all :)
Gotcha. Well, it's been even longer now and _zero_ random logouts. Seems to be certainly fixed :+1:
I'm having a problem with this approach @mjsarfatti...
When I load both files on my providers array at app.php, Laravel throws:
ReflectionException
Class session does not exist
When I do php artisan dump-auto, it also throws:
{"error":{"type":"ReflectionException","message":"Class session does not exist","file":"\/home\/heitor\/public_html\/project\/vendor\/laravel\/framework\/src\/Illuminate\/Container\/Container.php","line":476}}
How did you included these files to the project and how did you called them at app.php?
Which files you had to change to Laravel know and use these custom providers you wrote?
Hi, make sure you understand this guide first: http://laravel.com/docs/extending#ioc-based-extension.
I put SessionManager.php and SessionServiceProvider.php in /app/libraries/YourNamespace/Session. Also make sure you are including /app/libraries in your composer.json in the autoload/psr-0 section:
{
"name": "laravel/laravel",
"description": "The Laravel Framework.",
"keywords": ["framework", "laravel"],
"require": {
...
},
"autoload": {
"classmap": [
...
],
"psr-0": {
...
"YourNamespace": "app/libraries"
}
},
...
I still get this, cannot fix it with session.gc_maxlifetime = 86400 or by overriding the session manager.
May be related to those experiencing this effect:
http://yetanotherprogrammingblog.com/content/laravel-40-41-session-configuration-problem-solved
So, to recap, in Laravel 4.0 session garbage collection (for native sessions anyway) was handled
natively by PHP and although Laravel overwrote a whole bunch of session-related PHP settings, the
one setting it did not overwrite was ‘session.gc_maxlifetime’. Setting Larvel’s ‘lifetime’ option to zero
was equivalent to setting ‘session.gc_cookie_lifetime’ to zero in the PHP ini file in order to make the
cookie session-only and server-side session lifetime was basically determined by the value of PHP's
‘session.gc_maxlifetime’ setting.However, Laravel 4.1 implements its own garbage collection and uses the value of ‘lifetime’ (along
with the ‘lottery’ settings, which are the equivalent to PHP's probability and divisor settings) to control
garbage collection. Laravel runs a lottery every time it finishes processing a request and if the right
number comes up, it deletes all sessions that are older than the number of minutes specified by
‘lifetime’.
It wasn't my case, I had started with 4.1, I think the problem was related to multiple parallel requests that file based session driver cannot handle. Changing it to cookie or redis or db fixed it, so if there are people having that kind of problem, try using other session drivers.
In my case, output_buffering on the server was off. There were 'headers already sent' that were preventing the sessions from working properly.
I added ob_start(); at the top of my index.php and everything seems to be working fine.
But why were headers sent? Are you echo'ing stuff in your app? Maybe check why the headers are being sent in the first place..
I have Same issue laravel 5.2 "Sessions randomly expire"
1 minit expire Sessions
@patelsagar yep. Same issue here, do you redirect() after you set sessions? because I do.
controller1
redirect()->route("controller2")
Sometimes causes the 500 to popup
@tarreislam i don't get you. what you want to explain.
i am getting this too, just locally, using homestead, this does not happen on my production server.
laravel 5.2.45
I am getting the same issue, has anyone found a solution to this issue?
@Kreshnik I solved the problem by switching to Redis and moved from php 5.3 > 7.0
i am using php 7, will try redis, thanks!
@tarreislam i will guess that you are on version 4.2, maybe less.. a don't believe that you can run laravel 5.2 on php 5.3
when i was using laravel 4.2 this was not an issue, everything worked fine.
@CesarLanderos My bad, I meant PHP 5.5.9 -> PHP 7 together with laravel 5.2*
I have some similar issues (using file sessions driver)
I switch to AWS ec2 instance, and from then, I lost the session randomly.
Everything was Ok in Laravel 5.2 with PHP 5.6 in another machine (Azure), I guess the problem appeared with PHP 7.0.
I'm sorry for the information, but I don't know from where it is from.
@Luddinus change to a more stable session driver like redis, that worked for me
@CesarLanderos Yep I'll try, the thing is I'm a little scared because the application is already in production...
@Luddinus well, do the switch when there are less concurrent users, schedule a maintenance date, the worst that can happen is that they have to log-in again.
Well, I switch to "database" driver that was easier than redis to configure... seem's ok at this time.
Switched the sessions te Redis (instead of file) today. Sessions has been working fine for 6 hours now. So Redis it is!
In my case the CSRF is keep changing is this relative to this thread, I used laravel caffeine and it's still expire and CSRF changed.
Any help
I also have the same problem with Laravel 5.7. Why this issue was closed without any solution.
Most helpful comment
I also have the same problem with Laravel 5.7. Why this issue was closed without any solution.