web php
Session Regeneration
Session regeneration means replacing the current session ID with a new one while keeping the session data. The browser receives a new session cookie, and the application continues using the same $_SESSION values under a fresh identifier.
You use regeneration when the old identifier should no longer be trusted enough for the next phase of the user's session. Login is the most common example, but it is not the only one.
What changes and what stays
The session ID changes. The session data normally stays.
That distinction matters. Regeneration is not the same as logout, and it is not the same as clearing $_SESSION. It is a way to keep useful state while moving it behind a new identifier.
<?php
declare(strict_types=1);
session_start();
$_SESSION['cart_items'] = ['book', 'keyboard'];
session_regenerate_id(true);
$_SESSION['last_regenerated_at'] = time();
echo 'Cart items: ' . implode(', ', $_SESSION['cart_items']) . PHP_EOL;
// Prints:
// Cart items: book, keyboard
The true argument asks PHP to delete the old session data. This is usually what you want after a security-sensitive transition.
Regenerate after important transitions
Good regeneration points are tied to a change in trust or privilege:
- successful login
- completing two-factor authentication
- switching into an admin or impersonation mode
- confirming a password before a sensitive action
- periodic rotation during very long sessions
For login, the typical flow is:
<?php
declare(strict_types=1);
session_start();
$credentialsAreValid = true;
$userId = 42;
if ($credentialsAreValid) {
session_regenerate_id(true);
$_SESSION['user_id'] = $userId;
$_SESSION['authenticated_at'] = time();
}
echo 'Authenticated: ' . ($_SESSION['user_id'] ?? 'no') . PHP_EOL;
// Prints:
// Authenticated: 42
The call must happen before output because PHP needs to send a new session cookie header.
Do not regenerate on every request
Regeneration has a cost. If a browser makes two requests at nearly the same time and one request changes the session ID, the other request may still use the old ID. That can create confusing bugs where users are unexpectedly logged out or session changes are lost.
This is why regeneration belongs at meaningful moments. It should not be a blanket line added to every page.
For long sessions, some applications rotate occasionally using a timestamp:
<?php
declare(strict_types=1);
$session = [
'last_regenerated_at' => time() - 4000,
];
$rotationInterval = 60 * 30;
$shouldRegenerate = ($session['last_regenerated_at'] ?? 0) < time() - $rotationInterval;
if ($shouldRegenerate) {
$session['last_regenerated_at'] = time();
}
echo $shouldRegenerate ? 'Rotate session ID' : 'Keep current session ID';
// Prints:
// Rotate session ID
In a real request, the rotation branch would call session_regenerate_id(true) before updating $_SESSION['last_regenerated_at'].
Deleting the old session
session_regenerate_id(true) and session_regenerate_id(false) both create a new ID. The difference is whether PHP should delete the old session data.
Use true for security-sensitive transitions such as login. Keeping the old session around can leave the previous ID usable for longer than needed.
There are rare cases where deleting the old session immediately can cause issues with unstable networks or parallel requests. Those cases need deliberate handling, not a random switch to false without understanding the risk.
What to check in a project
Search for session_regenerate_id. Check that calls are near login, role elevation, password confirmation, or a deliberate rotation policy.
Check the argument. For login, true is usually expected.
Check the order. Regeneration should happen after credentials are accepted and before storing authenticated session state.
Check output. Any whitespace or HTML sent before regeneration can prevent PHP from sending the new cookie.
What you should be able to do
After this lesson, you should be able to explain what session regeneration changes, keep it separate from logout, choose sensible regeneration points, avoid regenerating on every request, and understand why the true argument matters.
Practice
Task: Decide When To Regenerate
Write a small PHP script that decides whether a session should be regenerated for different events.
Requirements
- Use
declare(strict_types=1);. - Create a function that accepts an event name and the last regeneration timestamp.
- Regenerate for
login,admin_mode, and old sessions. - Do not regenerate for an ordinary page view when the last regeneration was recent.
- Return a short reason with the decision.
- Print at least three scenarios.
Check Your Work
Run the script and confirm that regeneration is tied to meaningful transitions rather than every request.
Show solution
This solution treats regeneration as a policy decision. The calling code could use the decision to call session_regenerate_id(true) in a real request.
<?php
declare(strict_types=1);
/**
* @return array{regenerate: bool, reason: string}
*/
function regenerationDecision(string $event, int $lastRegeneratedAt, int $now): array
{
if (in_array($event, ['login', 'admin_mode'], true)) {
return ['regenerate' => true, 'reason' => 'privilege changed'];
}
$thirtyMinutes = 60 * 30;
if ($lastRegeneratedAt < $now - $thirtyMinutes) {
return ['regenerate' => true, 'reason' => 'session rotation interval passed'];
}
return ['regenerate' => false, 'reason' => 'no trust boundary changed'];
}
$now = 1_700_000_000;
$scenarios = [
['login', $now - 60],
['page_view', $now - 60],
['page_view', $now - 4000],
['admin_mode', $now - 120],
];
foreach ($scenarios as [$event, $lastRegeneratedAt]) {
$decision = regenerationDecision($event, $lastRegeneratedAt, $now);
$answer = $decision['regenerate'] ? 'regenerate' : 'keep';
echo $event . ': ' . $answer . ' because ' . $decision['reason'] . PHP_EOL;
}
// Prints:
// login: regenerate because privilege changed
// page_view: keep because no trust boundary changed
// page_view: regenerate because session rotation interval passed
// admin_mode: regenerate because privilege changed
In production code, the regenerate branch would call session_regenerate_id(true) before updating the session timestamp.
Why This Works
The examples prove the important balance: regenerate when trust changes or a rotation interval expires, but keep the current ID for ordinary recent page views.