Implementing Session Timeout With PHP

PHP aims to make things simple.  So you’d think something like specifying session timeout would also be simple.  Unfortunately it can be a little tricky depending on your circumstances.  For example, if you Google php session timeout, you’ll find no shortage of people that found trouble implementing session timeouts with PHP.

We found ourselves in the same situation when we recently ported TimePanel to another framework.  Soon after, some users began complaining that they were being logged out too soon.  A quick check confirmed that our PHP session and cookie settings were still the same, but we did find that the previous framework handled session timeouts, whereas the new one didn’t.  After some minor code diffs and a little research, we decided we needed to implement our own session timeout logic.

Understanding How PHP Handles Sessions

Before I explain what we did, it’s important to understand how PHP handles session data; in particular, when sessions expire and are subsequently cleared from the server.  Since PHP supports multiple forms of session storage (file, database, etc.), this post will assume the default storage mechanism: the file system.

How Sessions Are Created

In their simplest form, a session consists of 2 components:

  1. A session file created and stored on the server, which is named after the session’s id.
  2. Some means for the client to identify its session id to the server.  This is usually in the form of a phpsessid cookie or url parameter (note: of the two, a cookie is the default method and considered more secure).  We’ll assume a cookie is used from this point forward.

The typical way a session is started is by calling PHP’s session_start().  What happens at that point is PHP looks for a phpsessid cookie.  If one is found, it uses that id to lookup an existing session file on the server.  If it finds one, then an existing session is successfully linked, and the session file that was just found is used.

If either cookie or session file aren’t found, PHP has no way to link to a previous session, so a new one is created.  That means a new session file is created, with a new session id, and a new phpsessid cookie is set to link the browser’s session to the new file on the server.

Any subsequent web requests will follow the same routine, either successfully linking to a previous session or creating a new one.

Understanding Session Duration

Now that we understand how sessions are created and the 2 primary components in play, we can start to understand how session duration is specified and managed.

Most conversations about this subject usually begin with 2 php.ini settings:

  1. session.cookie_lifetime
  2. session.gc_maxlifetime

Each one is related to one of the session components mentioned above, so it’s important to understand both of them, and that collectively they aren’t sufficient to enforce a session duration.

The first setting, session.cookie_lifetime is simply a duration, in seconds, that PHP sets for the phpsessid cookie expiry.

The second setting, session.gc_maxlifetime, is more complex.  On the surface, it specifies how long session files can live on the server before PHP’s garbage collector sees it as a garbage candidate.  I say candidate, because a session file can, indeed, live beyond this point; it’s all a matter of probability.

You see, PHP’s session garbage collector (what’s responsible for deleting session files) doesn’t run 100% of the time; doing so would be too resource intensive.  Instead, it’s designed to run on a per-request-probability basis, or as part of a user-defined process.

  • When on a per-request basis, session.gc_probability and session.gc_divisor ini settings come into play.  Their role is to compute the probability on whether or not the garbage collector runs on this request.  In general, a higher probability means a given request is more likely to initiate the garbage collector.  That also means the garbage collector winds up running more often.
  • When leveraging a user-defined process, such as a cron job, that probability becomes obsolete, giving you full control over when session files are deleted.  You can essentially set a cron job to run on a set schedule to authoritively delete session files on a fixed schedule.

Going back to session.gc_maxlifetime and session.cookie_lifetime … the purpose of both is to allow you to specify a “soft” duration on both session components (the phpsessid cookie, and the session file on the server), and to give you some level of control over when the session garbage collector runs.

So why aren’t these 2 sufficient to enforce session timeout?  Because neither are 100% reliable in deleting their respective session components after a given time frame.

Since the phpsessid cookie exists on the client, it can be manipulated or deleted at any time.  Plus, if there’s no session file on the server that corresponds with the cookie’s session id (e.g. if the session file on the server is deleted for whatever reason), the cookie is ultimately useless. So alone, session.cookie_lifetime isn’t sufficient.

And as mentioned above, session.gc_maxlifetime doesn’t enforce session deletion very strictly at all – unless overridden by a user defined process, it bases session deletion on probability!

The Solution: Implement Your Own Session Timeout

So despite the session ini settings available, if you want a reliable session timeout, you’re forced to implement your own.  Fortunately doing so is pretty easy.

First, set session.gc_maxlifetime to the desired session timeout, in seconds.  E.g. if you want your sessions to timeout after 30 minutes, set session.gc_maxlifetime to 1800 (60 seconds in a minute * 30 minutes = 1,800 seconds).  What this does is ensure a given session file on the server can live for at least that long.

Second, and what a lot of other posts out there don’t mention, is that you also need to set session.cookie_lifetime to at least the same value (1,800 seconds, in this case).  Otherwise, the phpsessid cookie may expire before 30 minutes is up.  If that happens, the cookie is removed and the client has no way of identifying its session id to the server anymore. That effectively terminates the session before our 30 minute session window.

Third, add the following code to your app’s entry point, or any point in your app that’s executed on every request (usually an index.php file, front controller, bootstrap file, etc.).

$time = $_SERVER[‘REQUEST_TIME’];

/**
 * for a 30 minute timeout, specified in seconds
 */

$timeout_duration = 1800;

/**
 * Here we look for the user’s LAST_ACTIVITY timestamp. If
 * it’s set and indicates our $timeout_duration has passed,
 * blow away any previous $_SESSION data and start a new one.
 */

if (isset($_SESSION[‘LAST_ACTIVITY’]) && ($time $_SESSION[‘LAST_ACTIVITY’]) > $timeout_duration) {
  session_unset();    
  session_destroy();
  session_start();    
}
   
/**
 * Finally, update LAST_ACTIVITY so that our timeout
 * is based on it and not the user’s login time.
 */

$_SESSION[‘LAST_ACTIVITY’] = $time;

What that does is keep track of the time a user’s session started.  That’s tested on every request to see if their session has expired a 30 minute window.  If so, a new session is created. This might also be where you’d handle re authenticating the user somehow, if needed, usually by giving them a login expired, or login UI, of some sort.

And that’s it.  For most, understanding the ini settings, and why they’re not effective, is usually more taxing than the code involved to get a timeout working.

Conclusion

If you want reliable session timeouts, ultimately you’ll need to implement your own timeout logic.  Most frameworks make session timeouts very easy to handle, but even if your code doesn’t, you can implement one with a handful of code.

Hopefully this post has helped shed some light on how PHP manages sessions, and allows you to implement a session timeout with out too much fuss.

This entry was posted in General. Bookmark the permalink.

17 Responses to Implementing Session Timeout With PHP

  1. sudhanshu says:

    i need to implement the session timeout part in my project how can i do?please send me the entire code of this one ……

  2. Alexxus says:

    Thank you so much for this article! I’ve read a lot on stackoverflow about session lifetime but never really understood how it works, especially how gc_maxlifetime correlates with the cookie_lifetime and the garbage collector. After reading your article, it’s crystal clear! Thank you!

  3. George says:

    This is a great breakdown, and very clear! I really appreciate you taking the time to write this.

    One problem I am having with my host is the OPPOSITE of what you are saying.
    The sessions my users are experiencing are timing out BEFORE they should.
    This has been happening for sometime, regardless of how high I set the session.gc_maxlifetime and session.cookie_lifetime values.
    Currently, I have them both set to 7200, which should be 2 hours.
    I can confirm via phpinfo that they are both set and that the session.save_path is set to “/tmp”.

    However, users seem to be getting kicked back to the login screen (session expires) after around 30 minutes or so. I can’t pinpoint the exact time they are getting their session expired, but it is way below the 7200 seconds mark.

    Can you think of anything else I should look into? Any other direction to point me in? I am at a loss for where to turn next.

    • mzarate says:

      Hi George,

      Indeed there a couple things you can try.

      The first is to update your code based on the snippet in this blog post; it’s been revised, and that included a fix. This line:

      $_SESSION[‘LAST_ACTIVITY’] = $time;

      … is now outside of the if block. It never should have been inside there; my apologies.

      The second, is that you may have a cron job deleting session files for you. Part of how I’d diagnose that is seeing if the user’s session file still exists on the server after they’re prematurely timed out. If you find it’s been removed, then a cron job may be doing that, in which case I recommend reading this:

      http://solutionfactor.net/blog/2014/02/08/session-files-unexpectedly-deleted/

      Otherwise, if the session file remains even after they’ve been timed out, then the issue is likely w/their cookie. Maybe it isn’t being set w/the duration you expect (maybe due to a framework override?).

      Hope this helps.

      • George says:

        Thank you for taking the time to respond and help.
        I wanted to post back here to let you know what eventually fixed my problem.

        I found that keeping everything else the same, but setting the session.cookie_lifetime to ‘0’ was what did the trick!

        That settings is, essentially, keeping the cookie and session alive forever until the user closes their browser completely. While I did not need the session to stay alive forever, I did want to keep it open longer, so this solved my issue.

        Just wanted to post that in case it helps anyone else who has the same problem I was having down the road.

  4. Kiran Shiveshwar says:

    I have made changes in php.ini
    session.gc_maxlifetime=10000

    By default session.cookie_lifetime is set to 0

    What I read is that if session.cookie_lifetime is set to 0 , the session id is active till the browser is open.
    My query is will session id expire after 10000 seconds or it will never expire till the browser is open since session.cookie_lifetime is set to 0

  5. Anvar says:

    Thanks! You helped me a lot.

  6. Dallas DeBruin says:

    George,

    Thank-you for the clear-cut post. Absolutely saved me hours on hours of reading through reference docs.

  7. Hello,

    I know this comment might be seriously outdated, however, as someone new to PHP, I appreciate the concise information. I will implement, definitely.

    -Ralph

  8. Nick says:

    Good Day,
    Just wanted to say ‘thank you’, this article was exactly what I needed to understand how the php time out works. Just built my first MVC application and now, thanks to you, has a working session time out.

    All the best!
    Nick

  9. Jared says:

    I also wanted to say thanks. Very well written and helped me understand how it all works.

  10. Kade says:

    Very helpful tutorial, thanks!

  11. Arbazkhan says:

    Thanks, for this good stuff…

  12. Didier says:

    Thanks for taking the time to write such a clear explanation on session management in php!

  13. Deniz Gezmis says:

    Many thanks for this write up. Great article.

  14. Sohal says:

    Where should I declare my session once in my project so I can recall when I will need? I am using Adobe dreamwave .

Leave a Reply

Your email address will not be published. Required fields are marked *