Thursday, July 10, 2008

PHP: Dealing With Premature Session Endage

I deployed a new app to a production server, and all was working great. Except, I noticed that after about 2 minutes of inactive, I'd be logged off. I've run into this before - PHP sessions ending prematurely, and it's always a pain to debug. Mostly, because it shouldn't be a pain to debug (there's really only one setting, and it should just work, right? Here's how I debugged the issue last night:

  • Put a page in place with a call to <?php phpinfo(); ?> in it. This was useful to confirm that session.gc_maxlifetime wasn't set to an absurdly small value.
  • Put the following test page in place:
    <?php
    session_start();
    
    echo "<pre>";
    echo "Session Save Path: " . session_save_path() . "\n\n";
    
    if(!g($_SESSION, 'x')) {
      echo "'x' not set....\n\n";
      $_SESSION['x'] = 'Hello World';
    } else {
      echo "'x' was found\n\n";
    }
    var_dump($_REQUEST);
    echo "</pre>";
    
    phpinfo();
    ?>
    I could <?php
    session_start();
    
    echo "<pre>";
    echo "Session Save Path: " . session_save_path() . "\n\n";
    
    if(!g($_SESSION, 'x')) {
      echo "'x' not set....\n\n";
      $_SESSION['x'] = 'Hello World';
    } else {
      echo "'x' was found\n\n";
    }
    var_dump($_REQUEST);
    echo "</pre>";
    
    phpinfo();
    ?>
    
    The first time you visit the page you'll be greeted with 'x' is not set. Hit reload again and it will say 'x' was found. I could then wait a few minutes and hit reload again - if it said 'x' is not set I know my session expired. This allowed me to test my environment without any of the complexity of the application getting in the way.
  • Put a large value for session.gc_maxlifetime in the .htaccess file:
    # Set how long sessions should stay valid: 1hr = 60sec * 60min = 3600sec
    php_value session.gc_maxlifetime 3600
    
    I visited my phpinfo() page to confirm that the setting took.

At this point, I was really scratching my head -- the sessions were set to expire in an hour, yet there were gone after two minutes. And while there are plenty of config values for sessions, none seemed to make the difference between sessions living and dying in that short a time.

Then I found this article which not only suggested setting session.max_gclifetime but also session.save_path. So, my debugging continued:

  • Connected to my server's cpanel, and using the file manager, created /home/<my-home-dir>/php_sessions/
  • Updated my .htaccess file to read:
    # Set how long sessions should stay valid: 1hr = 60sec * 60min = 3600sec
    php_value session.gc_maxlifetime 3600
    # Where to stash session data
    php_value session.save_path "/home/<my-username>/php_sessions"
    
  • Visited the phpinfo(); file page and confirmed that my setting took. I also visited my session test page and confirmed that a file was created in php_sessions.
  • Sure enough, after 15 minutes, the file was there and my session was still valid.

Problem solved - whoo!

If I had to guess what was going on, I'd suggest that on this shared server, someone or something was was deleting files out of the shared session folder on a very regular basis. Creating my own session.save_path bought me two key benefits: (1) those files didn't get deleted and (2) I could now closely inspect the creation and deletion of sessions, even downloading the session files and inspecting them manually.

Usually, php sessions Just Work - but it's nice to know that when things don't, there's a reasonably easy way to debug what's going on.

4 comments:

  1. Anonymous9:22 PM

    Premature session endage. Yeah. Chicks hate when that happens. :-P

    ReplyDelete
  2. I know.

    And Shira tells me that it happens to everyone, not just me. But, perhaps she's fibbing?

    ;-)

    ReplyDelete
  3. I found out that on Ubuntu the php5 package installs a /etc/cron.d/php5 file that'll remove 'untouched for 24min' session files on the :9 and :39 of every hour. So you have a minimum of 24min and max of 54min sessions. But it only looks in the default session file location.
    Maybe thats why changing the path worked?

    ReplyDelete
  4. Nate -

    That could definitely be it. Nice catch!

    -Ben

    ReplyDelete