How do I expire a PHP session after 30 minutes?
Asked Answered
T

18

1176

I need to keep a session alive for 30 minutes and then destroy it.

Trace answered 6/2, 2009 at 13:14 Comment(3)
Please note that at least two settings are crucial to setting the session time, and maybe three. The two certainly crucial ones are session.gc_maxlifetime and session.cookie_lifetime (where 0 is not the same as some long number). For complete, 100% certainty of allowing long times, it may also be necessary to set the session.save_path, due to varying OS-controled cleanup time on the /tmp directory where session files get stored by default.Behan
I don't understand why you want to expire the session. If you worry the user leaves his computer without logging out, and an unauthorized user takes over his computer, the session expiration on your site will not prevent the hijacker access the victim's files on the disk.Hurwitz
@Gqqnbig: not every session is set for the reasons you are indirectly suggesting hereChaps
P
1786

You should implement a session timeout of your own. Both options mentioned by others (session.gc_maxlifetime and session.cookie_lifetime) are not reliable. I'll explain the reasons for that.

First:

session.gc_maxlifetime
session.gc_maxlifetime specifies the number of seconds after which data will be seen as 'garbage' and cleaned up. Garbage collection occurs during session start.

But the garbage collector is only started with a probability of session.gc_probability divided by session.gc_divisor. And using the default values for those options (1 and 100 respectively), the chance is only at 1%.

Well, you could simply adjust these values so that the garbage collector is started more often. But when the garbage collector is started, it will check the validity for every registered session. And that is cost-intensive.

Furthermore, when using PHP's default session.save_handler files, the session data is stored in files in a path specified in session.save_path. With that session handler, the age of the session data is calculated on the file's last modification date and not the last access date:

Note: If you are using the default file-based session handler, your filesystem must keep track of access times (atime). Windows FAT does not so you will have to come up with another way to handle garbage collecting your session if you are stuck with a FAT filesystem or any other filesystem where atime tracking is not available. Since PHP 4.2.3 it has used mtime (modified date) instead of atime. So, you won't have problems with filesystems where atime tracking is not available.

So it additionally might occur that a session data file is deleted while the session itself is still considered as valid because the session data was not updated recently.

And second:

session.cookie_lifetime
session.cookie_lifetime specifies the lifetime of the cookie in seconds which is sent to the browser. […]

Yes, that's right. This only affects the cookie lifetime and the session itself may still be valid. But it's the server's task to invalidate a session, not the client. So this doesn't help anything. In fact, having session.cookie_lifetime set to 0 would make the session’s cookie a real session cookie that is only valid until the browser is closed.

Conclusion / best solution:

The best solution is to implement a session timeout of your own. Use a simple time stamp that denotes the time of the last activity (i.e. request) and update it with every request:

if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > 1800)) {
    // last request was more than 30 minutes ago
    session_unset();     // unset $_SESSION variable for the run-time 
    session_destroy();   // destroy session data in storage
}
$_SESSION['LAST_ACTIVITY'] = time(); // update last activity time stamp

Updating the session data with every request also changes the session file's modification date so that the session is not removed by the garbage collector prematurely.

You can also use an additional time stamp to regenerate the session ID periodically to avoid attacks on sessions like session fixation:

if (!isset($_SESSION['CREATED'])) {
    $_SESSION['CREATED'] = time();
} else if (time() - $_SESSION['CREATED'] > 1800) {
    // session started more than 30 minutes ago
    session_regenerate_id(true);    // change session ID for the current session and invalidate old session ID
    $_SESSION['CREATED'] = time();  // update creation time
}

Notes:

  • session.gc_maxlifetime should be at least equal to the lifetime of this custom expiration handler (1800 in this example);
  • if you want to expire the session after 30 minutes of activity instead of after 30 minutes since start, you'll also need to use setcookie with an expire of time()+60*30 to keep the session cookie active.
Pledge answered 13/8, 2009 at 9:24 Comment(30)
How could you alter this if you wanted to check "inactive time"? In other words, the user logs in, and as long as they continue to use the site, it will not log them out. However if they are inactive for 30 mins it will log them out?Harbison
@Metropolis: Use something like $_SESSION['LAST_ACTIVITY'] similar to $_SESSION['CREATED'] where you store the time of the last activity of the user but update that value with every request. Now if the difference of that time to the current time is larger that 1800 seconds, the session has not been used for more than 30 minutes.Pledge
@Pledge isnt it better to use session_unset instead of $_SESSION = array();?Harbison
@Metropolis: session_unset does the same as $_SESSION = array().Pledge
@Pledge - I´m a bit confused, shouldn´t you use your code in combination with ini_set('session.gc-maxlifetime', 1800)? Otherwise your session information could get destroyed while your session is still supposed to be valid, at least if the ini setting is the standard 24 minutes. Or am I missing something?Oscillograph
@jeron: Yes, you should. But note that session.gc_maxlifetime depends on the file’s last modification date if the session save handler files is used. So session.gc_maxlifetime should be at least equal to the life time of this custom expiration handler.Pledge
In case if I want to make the session live till the browser is closed what should I do? I don't understand it as the gc checks if session is expired on session_start, but I can't modify the session before calling session_start, so modifying the session data after calling session_start would result in expired session anyway?Sinclair
@Richards: That would be the default behavior of a session cookie (a cookie that lasts only during the browser session). Just omit an expiration value for the cookie (or in case of PHP’s session.cookie_lifetime: set it to 0) and set the session’s lifetime to a large value.Pledge
@Pledge : this question is heavily referenced/linked to when similar questions regarding session are asked. Unfortunately, the quirk where the 0 setting is vastly different from a long setting in seconds, which you make in your Feb 3rd comment, is not made clear almost anywhere else. Would you mind making reference to it in your answer? Or make the answer CW or something so that we can make this Q&A a great resource for all the Session F.U.D. out there?Behan
@Pledge this is a great tutorial.. I would however recommend against using session.gc_maxlifetime as this is resource heavy.. But the second part is great.Heroic
This is a great answer, one thing to add is that session_unset() appears to have been deprecated in favor of $_SESSION = array(); see the note here: php.net/manual/en/function.session-destroy.phpDelphine
Furthermore, I've found it necessary to include a session_start() call after the session_destroy() call, but I don't quite understand why just yet..Delphine
#5081525 Compared with the Destroying a Session , which is better? If you're going to destroy a session (on logout for example), make sure you destroy it thoroughly. This includes unsetting the cookie. Using session_destroy: function destroySession() { $params = session_get_cookie_params(); setcookie(session_name(), '', time() - 42000, $params["path"], $params["domain"], $params["secure"], $params["httponly"] ); session_destroy(); }Paramour
@RajuGujarati Most important is that you destroy the session on the server side; revoking/deleting the cookie with the session ID may fail. Similarly, session_regenerate_id(true) generates a new session ID and destroys the old session on the server side.Pledge
@Pledge this technique depends on the user loading a page before he is notified of his expiration or checking on pageload. I need one that expires weather the user loads a page or not. this is because other users are notified when a session expires, and data is recorded in the database for further use.Padrone
What about changing the /etc/cron.d/php5 ?Arid
@Sosukodo The issue is not with the garbage collector but with the technique that is used to distinguish timed-out sessions, which is entirely missing in PHP’s built-in session management.Pledge
if you use sesion_unset() and session_destroy() wont you delete all of the variables in $_SESSION[] ????? and that would completely make $SESSIONS pointless :SDement
Setting $_SESSION['LAST_ACTIVITY'] after destroying the session is fishy. Putting it in an else clause would be better.Poncho
How do you tell the difference between a first-time visitor and a visitor with a php-expired session? In other words, isset($_SESSION['LAST_ACTIVITY']) will be false for either, but the program will continue, as if it was a first timer.Ephesus
@Pledge yes but what you do if you have two different types of users...and for the one the requirement is 1 hour(for example) and for the other 4 hours...what value I must put in session.gc_maxlifetime?Spier
@DimitrisPapageorgiou The larger one, otherwise the longer term session would be ended prematurely.Pledge
@Gumbo...so I put the larger one in session.maxlifetime and still use different value for the different users...thanksSpier
What is the best way to code this up? I use sessions but do not normally touch cookies directly. you'll also need to use setcookie with an expire of time()+60*30 to keep the session cookie active.Edema
$_SESSION['CREATED'] or $_SESSION['LAST_ACTIVITY'] will return an error if session is expired.Wendell
11 years ago question and answer... I can see many of you ask what will happen if user just login and stay inactive... In asp.net I created javascript function who will made ajax call to webapi, in this case can call some php file where you'll do check like in provided code by Gumbo.Freda
"But the garbage collector is only started with a probability of session.gc_probability divided by session.gc_divisor. And using the default values for those options (1 and 100 respectively), the chance is only at 1%." "By default the session ends after 24 minutes." After 24 minutes of inactivity and using the default values 'the chance is still only at 1%'. Does this means even after 24 minutes the session file is not deleted ?Glimmer
There are "session.cookie-lifetime" and "session.gc-maxlifetime" in php.ini (I mean that you should use "-" instead of "_")Motile
This works well. I've used unset($_SESSION); instead of session_unset(); so I can use isset($_SESSION); for further validationChapin
what about the case when we want to implement 2 types of session durations for 2 different types of users....since session.gc_maxlifetime accepts only 1 valueSpier
S
156

Simple way of PHP session expiry in 30 minutes.

Note : if you want to change the time, just change the 30 with your desired time and do not change * 60: this will gives the minutes.


In minutes : (30 * 60)
In days : (n * 24 * 60 * 60 ) n = no of days


Login.php

<?php
    session_start();
?>

<html>
    <form name="form1" method="post">
        <table>
            <tr>
                <td>Username</td>
                <td><input type="text" name="text"></td>
            </tr>
            <tr>
                <td>Password</td>
                <td><input type="password" name="pwd"></td>
            </tr>
            <tr>
                <td><input type="submit" value="SignIn" name="submit"></td>
            </tr>
        </table>
    </form>
</html>

<?php
    if (isset($_POST['submit'])) {
        $v1 = "FirstUser";
        $v2 = "MyPassword";
        $v3 = $_POST['text'];
        $v4 = $_POST['pwd'];
        if ($v1 == $v3 && $v2 == $v4) {
            $_SESSION['luser'] = $v1;
            $_SESSION['start'] = time(); // Taking now logged in time.
            // Ending a session in 30 minutes from the starting time.
            $_SESSION['expire'] = $_SESSION['start'] + (30 * 60);
            header('Location: http://localhost/somefolder/homepage.php');
        } else {
            echo "Please enter the username or password again!";
        }
    }
?>

HomePage.php

<?php
    session_start();

    if (!isset($_SESSION['luser'])) {
        echo "Please Login again";
        echo "<a href='http://localhost/somefolder/login.php'>Click Here to Login</a>";
    }
    else {
        $now = time(); // Checking the time now when home page starts.

        if ($now > $_SESSION['expire']) {
            session_destroy();
            echo "Your session has expired! <a href='http://localhost/somefolder/login.php'>Login here</a>";
        }
        else { //Starting this else one [else1]
?>
            <!-- From here all HTML coding can be done -->
            <html>
                Welcome
                <?php
                    echo $_SESSION['luser'];
                    echo "<a href='http://localhost/somefolder/logout.php'>Log out</a>";
                ?>
            </html>
<?php
        }
    }
?>

LogOut.php

<?php
    session_start();
    session_destroy();
    header('Location: http://localhost/somefolder/login.php');
?>
Sismondi answered 13/9, 2011 at 6:39 Comment(9)
Combining logic and presentation is ill-advised in this day and age when MVC is the norm.Plaque
Maybe I'm missing something elementary about sessions but what good does this do if sessions are destroyed every 30 minutes by the OS?Arid
@stillstanding Speak for yourself [smile] I view MVC as an abomination.Arid
Is MVC a good idea even when the project is small, with a single programmer? I feel like I should be making my own projects in the MVC model (or solve the problem THEN make it MVC) but with a lack of experience with MVC it just becomes a mental block "How do I make this MVC?" and a distraction from the initial goal/problem requiring a solution.Rectory
@stillstanding another mention is that on Login.php headers are sent AFTER the content, which is bad.Chayachayote
@Plaque Not at all. Mixing logic with markup is essentially legitimate pattern in PHP. Moreover, that has been the whole point of PHP form the very beginning. And if you look at the most popular frontend framework now days: ReactJS, you will see it does the same.Ciri
@Ciri Sounds like a lame excuse. If a block of wood can support a person, you would just use it as a chair without thinking there's a better solution because others do it anyway. For some people, there's something called progressive engineering, that's why we have chairs and not benches or plain wooden logs in the living room. And ReactJS isn't really the great pyramid. JSX is a Frankenstein IMO.Plaque
@bsosca as many here should, you should spend more time worrying about solutions to the problems and allowing the OP to figure that out than hijacking a question to make some point you think is valid ;-)Disorderly
@bbosca - NOPE!!!! logic and presentation are just fine together. This MVC and all that is just not practical!Gandzha
V
50

Is this to log the user out after a set time? Setting the session creation time (or an expiry time) when it is registered, and then checking that on each page load could handle that.

E.g.:

$_SESSION['example'] = array('foo' => 'bar', 'registered' => time());

// later

if ((time() - $_SESSION['example']['registered']) > (60 * 30)) {
    unset($_SESSION['example']);
}

Edit: I've got a feeling you mean something else though.

You can scrap sessions after a certain lifespan by using the session.gc_maxlifetime ini setting:

Edit: ini_set('session.gc_maxlifetime', 60*30);

Vina answered 6/2, 2009 at 13:20 Comment(2)
session.gc-maxlifetime is probably the best way to go.Synecious
There are some issues with the session cookie lifetime, most notably, it relies on the client to enforce it. The cookie lifetime is there to allow the client to clean up useless/expired cookies, it is not to be confused with anything security related.Babu
K
28

This post shows a couple of ways of controlling the session timeout: http://bytes.com/topic/php/insights/889606-setting-timeout-php-sessions

IMHO the second option is a nice solution:

<?php
/***
 * Starts a session with a specific timeout and a specific GC probability.
 * @param int $timeout The number of seconds until it should time out.
 * @param int $probability The probablity, in int percentage, that the garbage 
 *        collection routine will be triggered right now.
 * @param strint $cookie_domain The domain path for the cookie.
 */
function session_start_timeout($timeout=5, $probability=100, $cookie_domain='/') {
    // Set the max lifetime
    ini_set("session.gc_maxlifetime", $timeout);

    // Set the session cookie to timout
    ini_set("session.cookie_lifetime", $timeout);

    // Change the save path. Sessions stored in teh same path
    // all share the same lifetime; the lowest lifetime will be
    // used for all. Therefore, for this to work, the session
    // must be stored in a directory where only sessions sharing
    // it's lifetime are. Best to just dynamically create on.
    $seperator = strstr(strtoupper(substr(PHP_OS, 0, 3)), "WIN") ? "\\" : "/";
    $path = ini_get("session.save_path") . $seperator . "session_" . $timeout . "sec";
    if(!file_exists($path)) {
        if(!mkdir($path, 600)) {
            trigger_error("Failed to create session save path directory '$path'. Check permissions.", E_USER_ERROR);
        }
    }
    ini_set("session.save_path", $path);

    // Set the chance to trigger the garbage collection.
    ini_set("session.gc_probability", $probability);
    ini_set("session.gc_divisor", 100); // Should always be 100

    // Start the session!
    session_start();

    // Renew the time left until this session times out.
    // If you skip this, the session will time out based
    // on the time when it was created, rather than when
    // it was last used.
    if(isset($_COOKIE[session_name()])) {
        setcookie(session_name(), $_COOKIE[session_name()], time() + $timeout, $cookie_domain);
    }
}
Kenney answered 7/10, 2014 at 2:31 Comment(3)
Best solution ever! Finally someone who knows how to use the server session vars! Good job!Incontinent
@Incontinent I also have a heartbeat request sent to the server once per minute so the server knows the client is alive.Kenney
Clever. Thanks for the tips!Incontinent
D
25

Well i understand the aboves answers are correct but they are on application level, why don't we simply use .htaccess file to set the expire time ?

<IfModule mod_php5.c>
    #Session timeout
    php_value session.cookie_lifetime 1800
    php_value session.gc_maxlifetime 1800
</IfModule>
Devinne answered 15/7, 2015 at 6:16 Comment(1)
This method will update session every time i enter into the page?Mackoff
G
20

Use the session_set_cookie_params function to do this.

It is necessary to call this function before the session_start() call.

Try this:

$lifetime = strtotime('+30 minutes', 0);

session_set_cookie_params($lifetime);

session_start();

See more in: http://php.net/manual/function.session-set-cookie-params.php

Graz answered 13/5, 2016 at 12:43 Comment(0)
F
18
if (isSet($_SESSION['started'])){
    if((mktime() - $_SESSION['started'] - 60*30) > 0){
        //Logout, destroy session, etc.
    }
}
else {
    $_SESSION['started'] = mktime();
}
Ferret answered 6/2, 2009 at 13:21 Comment(0)
N
12

It's actually easy with a function like the following. It uses database table name 'sessions' with fields 'id' and 'time'.

Every time when the user visits your site or service again you should invoke this function to check if its return value is TRUE. If it's FALSE the user has expired and the session will be destroyed (Note: This function uses a database class to connect and query the database, of course you could also do it inside your function or something like that):

function session_timeout_ok() {
    global $db;
    $timeout = SESSION_TIMEOUT; //const, e.g. 6 * 60 for 6 minutes
    $ok = false;
    $session_id = session_id();
    $sql = "SELECT time FROM sessions WHERE session_id = '".$session_id."'";
    $rows = $db->query($sql);
    if ($rows === false) {
        //Timestamp could not be read
        $ok = FALSE;
    }
    else {
        //Timestamp was read succesfully
        if (count($rows) > 0) {
            $zeile = $rows[0];
            $time_past = $zeile['time'];
            if ( $timeout + $time_past < time() ) {
                //Time has expired
                session_destroy();
                $sql = "DELETE FROM sessions WHERE session_id = '" . $session_id . "'";
                $affected = $db -> query($sql);
                $ok = FALSE;
            }
            else {
                //Time is okay
                $ok = TRUE;
                $sql = "UPDATE sessions SET time='" . time() . "' WHERE session_id = '" . $session_id . "'";
                $erg = $db -> query($sql);
                if ($erg == false) {
                    //DB error
                }
            }
        }
        else {
            //Session is new, write it to database table sessions
            $sql = "INSERT INTO sessions(session_id,time) VALUES ('".$session_id."','".time()."')";
            $res = $db->query($sql);
            if ($res === FALSE) {
                //Database error
                $ok = false;
            }
            $ok = true;
        }
        return $ok;
    }
    return $ok;
}
Neurotomy answered 24/1, 2014 at 8:40 Comment(0)
A
10

Store a timestamp in the session


<?php    
$user = $_POST['user_name'];
$pass = $_POST['user_pass'];

require ('db_connection.php');

// Hey, always escape input if necessary!
$result = mysql_query(sprintf("SELECT * FROM accounts WHERE user_Name='%s' AND user_Pass='%s'", mysql_real_escape_string($user), mysql_real_escape_string($pass));

if( mysql_num_rows( $result ) > 0)
{
    $array = mysql_fetch_assoc($result);    

    session_start();
    $_SESSION['user_id'] = $user;
    $_SESSION['login_time'] = time();
    header("Location:loggedin.php");            
}
else
{
    header("Location:login.php");
}
?>

Now, Check if the timestamp is within the allowed time window (1800 seconds is 30 minutes)

<?php
session_start();
if( !isset( $_SESSION['user_id'] ) || time() - $_SESSION['login_time'] > 1800)
{
    header("Location:login.php");
}
else
{
    // uncomment the next line to refresh the session, so it will expire after thirteen minutes of inactivity, and not thirteen minutes after login
    //$_SESSION['login_time'] = time();
    echo ( "this session is ". $_SESSION['user_id'] );
    //show rest of the page and all other content
}
?>
Accentuation answered 21/5, 2015 at 11:20 Comment(0)
P
8

Please use following block of code in your include file which loaded in every pages.

$expiry = 1800 ;//session expiry required after 30 mins
    if (isset($_SESSION['LAST']) && (time() - $_SESSION['LAST'] > $expiry)) {
        session_unset();
        session_destroy();
    }
    $_SESSION['LAST'] = time();
Procto answered 26/8, 2016 at 6:55 Comment(1)
don't use sesssion_destroy without deleting the session cookies. Here in this Code better Clear the Session superglobal $_SESSION = array() Also don't use session_unset in newer PHP versions anymore.Overhasty
C
5

This was an eye-opener for me, what Christopher Kramer wrote in 2014 on https://www.php.net/manual/en/session.configuration.php#115842

On debian (based) systems, changing session.gc_maxlifetime at runtime has no real effect. Debian disables PHP's own garbage collector by setting session.gc_probability=0. Instead it has a cronjob running every 30 minutes (see /etc/cron.d/php5) that cleans up old sessions. This cronjob basically looks into your php.ini and uses the value of session.gc_maxlifetime there to decide which sessions to clean (see /usr/lib/php5/maxlifetime). [...]

Crosscurrent answered 13/11, 2020 at 8:57 Comment(0)
G
3

How PHP handles sessions is quite confusing for beginners to understand. This might help them by giving an overview of how sessions work: how sessions work(custom-session-handlers)

Glimmer answered 31/5, 2020 at 20:45 Comment(0)
T
1

Use this class for 30 min

class Session{
    public static function init(){
        ini_set('session.gc_maxlifetime', 1800) ;
        session_start();
    }
    public static function set($key, $val){
        $_SESSION[$key] =$val;
    }
    public static function get($key){
        if(isset($_SESSION[$key])){
            return $_SESSION[$key];
        } else{
            return false;
        }
    }
    public static function checkSession(){
        self::init();
        if(self::get("adminlogin")==false){
            self::destroy();
            header("Location:login.php");
        }
    }
    public static function checkLogin(){
        self::init();
        if(self::get("adminlogin")==true){
            header("Location:index.php");
        }
    }
    public static function destroy(){
        session_destroy();
        header("Location:login.php");
    }
}
Tsingyuan answered 27/12, 2018 at 10:0 Comment(1)
Note: You do not have to call session_destroy() from usual code. Cleanup $_SESSION array rather than destroying session data. Otherwise expect various side effects (e.g. if you do session_destroy and don't have session.use_strict_mode enabled you need to destroy also the session cookie also consider the warnings in the docu php.net/manual/en/function.session-destroy.phpOverhasty
C
0

Using timestamp...

<?php
if (!isset($_SESSION)) {
    $session = session_start();
} 
if ($session && !isset($_SESSION['login_time'])) {
    if ($session == 1) {
        $_SESSION['login_time']=time();
        echo "Login :".$_SESSION['login_time'];
        echo "<br>";
        $_SESSION['idle_time']=$_SESSION['login_time']+20;
        echo "Session Idle :".$_SESSION['idle_time'];
        echo "<br>";
    } else{
        $_SESSION['login_time']="";
    }
} else {
    if (time()>$_SESSION['idle_time']){
        echo "Session Idle :".$_SESSION['idle_time'];
        echo "<br>";
        echo "Current :".time();
        echo "<br>";
        echo "Session Time Out";
        session_destroy();
        session_unset();
    } else {
        echo "Logged In<br>";
    }
}
?>

I have used 20 seconds to expire the session using timestamp.

If you need 30 min add 1800 (30 min in seconds)...

Cartelize answered 21/3, 2018 at 10:40 Comment(0)
U
0

You can straight use a DB to do it as an alternative. I use a DB function to do it that I call chk_lgn.

Check login checks to see if they are logged in or not and, in doing so, it sets the date time stamp of the check as last active in the user's db row/column.

I also do the time check there. This works for me for the moment as I use this function for every page.

P.S. No one I had seen had suggested a pure DB solution.

Urba answered 29/3, 2019 at 5:39 Comment(0)
T
0

Here you can set the hours

$lifespan = 1800;
ini_set('session.gc_maxlifetime', $lifespan); //default life time
Terchie answered 20/6, 2021 at 1:16 Comment(0)
S
0

Set the timeout parameter on the cookie attached to the session. I did this in my accounts.php class and it works great.

function _setSession($aArgs) 
{    
    // create string from account array values
    $sVal = implode("|", $aArgs);
    
    // set account session cookie
    $ret = setcookie("cACCOUNT", $sVal, time()+(TIMEOUT / 2 ), "/", "", "0", "1" );
       
}
Seasickness answered 12/3 at 18:37 Comment(0)
C
-1

Just Store the current time and If it exceeds 30 minutes by comparing then destroy the current session.

Clumsy answered 24/1, 2020 at 8:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.