Managing PHP Sessions

In the not too distant past I was grappling with how to get PHP sessions running on Ubuntu to work as advertised in the manual. Here's some of my notes to pay it forward.

Background

Here's a little background on how sessions work in PHP:

PHP sessions are (by default) stored in files on disk—generally in a publicly accessible temp directory. Each time a session is started on the server, PHP may first clean up any sessions in that default location based on the garbage collection lifetime setting: gc_maxlifetime.

Since garbage collection can be costly from a performance perspective, this is only performed periodically. When garbage collection is ran is determined by two other settings:

  • Garbage Collection Probability (session.gc_probability)
  • Garbage Collection Divisor (session.gc_divisor)

If, for example, gc_probability is 1 and gc_divisor is 100 (as is the default), then the probability that garbage collection would run on any given script invocation would be 1 out of 100 times or 1%.

In theory, garbage collection is ran on every file in the session directory—regardless of which site the session belongs to. This means that if you have two or more sites sharing the same directory for session file, all sessions will be garbage collected according the the scripts with the shortest collection interval.

Where Session Files are Stored

You can specify a custom location for your session files by setting the session.save_path php ini directive. However, it you rely on the default value (which leaves session.save_path blank) the actual location session files get saved will vary depending on your OS.

The table below shows where different operating systems will store your session files.

OS Default Location
Default ( System temp directory )
Debian/Ubuntu /var/lib/php5
RHEL and CentOS /var/lib/php/session

You can always verify it yourself using the following from the command line:

php -r 'echo session_save_path()'  

Session Garbage Collection in Ubuntu / Debian Systems

In non-Debian based systems garbage collection is performed by PHP itself. In this case, PHP itself examines the session directory and cleans up the old session files. One implication of this is that PHP will need to have read/write access to the session directory.

On Debian systems, PHP's defaults for where session files are stored is changed to /var/lib/php5. This directory is locked down from a permissions standpoint session garbage collection in PHP is actually disabled—cleaning up the sessions in Debian systems is actually handled by a parallel crontab task.

The reason for this divergence stems from PHP's history of being used commonly on shared servers at discount hosting providers. Allowing each user on the server running PHP to access the temp directory had security implications which the separate cron entry and locked down session directory get around.

Getting Session Lifetimes to Work on Ubuntu / Debian

Given the above, you would think this is all you need to set a user session to be two weeks:

session_start([  
    'cookie_lifetime' => 1209600, // Keep in browser for 2 weeks
    'gc_maxlifetime'  => 1209600, // Keep on server for 2 weeks
    'save_path'       => '/my/custom/location'
]);

That, however, doesn't seem to work on Ubuntu. I don't know if it's the custom PHP session handling Debian uses or something to do with PHP iteself, but it seems that setting those values via the session_start() method do not really work. If you use the above, my experience has been that the sessions are not saved in your custom location as you would expect and are still housed in the default temp location.

Instead you should try this:

session_save_path("/my/custom/location");  
session_set_cookie_params(1209600);  
ini_set('session.gc_probability', 1);  
ini_set('session.gc_maxlifetime', 1209600);  
session_start();  

This seems to get your sessions into the right directory and everything is all good. Note the session.gc_probability. This is because Debian systems, by default will set the value to 0 and use a cron to clean up sessions for you. Setting it to 1 means that PHP's session garbage collection will run 1 out of every 100 session starts—assuming the gc_divisor setting is still at its default of 100.