When a user logs out, you must end the session completely. A half-finished logout can leave the old session alive.
Three steps
- Empty the data in
$_SESSION. - Delete the session cookie in the browser.
- Destroy the session on the server.
<?php
session_start();
$_SESSION = []; // 1. clear the data
if (ini_get("session.use_cookies")) {
$p = session_get_cookie_params();
setcookie(session_name(), "", [ // 2. expire the cookie
"expires" => time() - 42000,
"path" => $p["path"],
"domain" => $p["domain"],
"secure" => $p["secure"],
"httponly" => $p["httponly"],
"samesite" => $p["samesite"] ?: "Lax",
]);
}
session_destroy(); // 3. destroy the session on the server
What each step does
$_SESSION = []removes the data in the current request.session_destroy()alone does not empty the variable until the script ends.- Expiring the cookie stops the browser from sending the old ID.
session_destroy()deletes the stored session data on the server.
A logout function
<?php
function logout(): void
{
$_SESSION = [];
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), "", [
"expires" => time() - 42000,
"path" => $params["path"],
"domain" => $params["domain"],
"secure" => $params["secure"],
"httponly" => $params["httponly"],
"samesite" => $params["samesite"] ?: "Lax",
]);
}
session_destroy();
}
Call it from a POST request, not a plain link. A logout that works by GET can be triggered by an image on another site.
Remove only some data
To keep the session but forget one thing, use unset().
<?php
$_SESSION = ["user_id" => 5, "cart" => [1 => 2]];
unset($_SESSION["cart"]);
print_r($_SESSION);
Array
(
[user_id] => 5
)
Timeouts
Sessions can also end because the user did nothing for too long. Two ways:
1. Server side. session.gc_maxlifetime (in seconds) tells PHP how long an idle session may be kept. The cleanup does not run at the exact moment, so do not rely on it for exact times.
2. Your own check. Store the time of the last request and compare.
<?php
function isExpired(int $lastActivity, int $now, int $limit = 1800): bool
{
return ($now - $lastActivity) > $limit;
}
var_dump(isExpired(1000, 2000)); // 1000 seconds idle
var_dump(isExpired(1000, 3500)); // 2500 seconds idle
bool(false)
bool(true)
In a real page, you store $_SESSION["last_activity"] = time(); on every request, and log the user out when isExpired() is true.
Change the ID at login
Whenever someone logs in (or their privileges change), call session_regenerate_id(true). That prevents session fixation, explained in Session Fixation.