$_SESSION
What Is a Session? In simple terms: A session lets a website remember information about a visitor across multiple pages — for example, that they are logged in, what is in their shopping cart, or a mes
What Is a Session?
In simple terms: A session lets a website remember information about a visitor across multiple pages — for example, that they are logged in, what is in their shopping cart, or a message to show after a redirect. The information is stored on the server, and the browser only keeps a random ID that identifies its session.
Technically: session_start() creates or resumes a session. PHP identifies the session through a cookie (named PHPSESSID by default) that contains a long random session ID. Session data is stored server-side (in files by default) and is available in the $_SESSION superglobal. At the end of the request, PHP writes any changes back to storage.
Sessions vs Cookies
| Cookies | Sessions | |
|---|---|---|
| Where data is stored | In the browser | On the server |
| What the browser holds | The data itself | Only a session ID |
| Can the user read or edit the data? | Yes | No |
| Size limit | About 4 KB | Limited only by server storage |
| Lifetime | Until the expiry date | Until the session ends or expires on the server |
| Typical use | Preferences, "remember me" tokens | Login state, carts, flash messages, CSRF tokens |
Starting a Session and Using $_SESSION
Example: Visit Counter
<?php
// counter.php
session_start();
$_SESSION['visits'] = ($_SESSION['visits'] ?? 0) + 1;
echo "You have viewed this page {$_SESSION['visits']} time(s) in this session.";
Output on Three Consecutive Requests From the Same Browser
You have viewed this page 1 time(s) in this session.
You have viewed this page 2 time(s) in this session.
You have viewed this page 3 time(s) in this session.
What Happens Behind the Scenes
- First request: the browser has no session cookie.
session_start()generates a new random ID, creates an empty session, and sends aSet-Cookie: PHPSESSID=...header. - The script stores
visits = 1in$_SESSION; PHP saves it to the session storage when the script ends. - Next requests: the browser sends the
PHPSESSIDcookie.session_start()loads the matching data into$_SESSION, so the counter continues. - A different browser (or a private window) has a different session and starts again at 1.
Rules for session_start()
- Call it before any output, because it may need to send a cookie header (otherwise: "Session cannot be started after headers have already been sent").
- Call it on every page that reads or writes
$_SESSION. Without it,$_SESSIONis not loaded. - In larger applications, a front controller or bootstrap file calls it once for all pages.
Reading, Writing, and Removing Session Data
<?php
session_start();
// Write
$_SESSION['user_id'] = 42;
$_SESSION['cart'] = ['PEN-01' => 3, 'NB-A5' => 1];
// Read with defaults
$userId = $_SESSION['user_id'] ?? null;
$itemCount = array_sum($_SESSION['cart'] ?? []);
// Update nested data
$_SESSION['cart']['PEN-01'] += 2;
// Remove one value
unset($_SESSION['cart']['NB-A5']);
$_SESSION behaves like an ordinary array. Values can be strings, numbers, booleans, and arrays. Avoid storing objects that hold resources (database connections, file handles), because session data must be serialized to be saved.
Practical Example: Login and Logout
Logging In
<?php
// login.php
session_start();
function findUserByEmail(string $email): ?array
{
// In a real application, this runs a database query.
$users = [
'asha@example.com' => [
'id' => 7,
'name' => 'Asha',
'password_hash' => password_hash('Secure#2026', PASSWORD_DEFAULT),
],
];
return $users[strtolower($email)] ?? null;
}
$error = null;
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
$email = is_string($_POST['email'] ?? null) ? trim($_POST['email']) : '';
$password = is_string($_POST['password'] ?? null) ? $_POST['password'] : '';
$user = findUserByEmail($email);
if ($user !== null && password_verify($password, $user['password_hash'])) {
session_regenerate_id(true);
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['name'];
header('Location: /dashboard.php', true, 303);
exit;
}
$error = 'Invalid email or password.';
}
Protecting a Page
<?php
// dashboard.php
session_start();
if (!isset($_SESSION['user_id'])) {
header('Location: /login.php', true, 303);
exit;
}
echo 'Welcome back, ' . htmlspecialchars($_SESSION['user_name']) . '!';
Logging Out
<?php
// logout.php (should be triggered by a POST request with a CSRF token)
session_start();
$_SESSION = [];
$params = session_get_cookie_params();
setcookie(session_name(), '', [
'expires' => time() - 3600,
'path' => $params['path'],
'domain' => $params['domain'],
'secure' => $params['secure'],
'httponly' => $params['httponly'],
'samesite' => $params['samesite'],
]);
session_destroy();
header('Location: /login.php', true, 303);
exit;
Step-by-Step Explanation
- Login verifies the password with
password_verify()against a stored hash (see Password Hashing). The same error message is used for unknown emails and wrong passwords, so attackers cannot discover which emails are registered. session_regenerate_id(true)gives the session a new ID right after login and deletes the old session data file. This prevents session fixation, an attack where the attacker plants a known session ID in the victim's browser before they log in.- Only the user's ID and display name are stored — never the password or its hash.
- Protected pages check for
user_idand redirect visitors who are not logged in. - Logout empties
$_SESSION, deletes the session cookie (with the same parameters it was created with), and destroys the server-side data. Calling onlysession_destroy()would leave the data in$_SESSIONfor the rest of the request and the cookie in the browser.
Flash Messages
A flash message is stored in the session, shown once after a redirect, and then removed:
<?php
function flash(string $key, ?string $message = null): ?string
{
if ($message !== null) {
$_SESSION['_flash'][$key] = $message;
return null;
}
$value = $_SESSION['_flash'][$key] ?? null;
unset($_SESSION['_flash'][$key]);
return $value;
}
// After saving a form: flash('success', 'Profile updated.'); then redirect
// On the next page: if ($msg = flash('success')) { echo htmlspecialchars($msg); }
This works together with the Post/Redirect/Get pattern described in \$_POST.
Secure Session Configuration
Configure the session cookie before calling session_start():
<?php
session_set_cookie_params([
'lifetime' => 0, // until the browser closes
'path' => '/',
'secure' => true, // HTTPS only
'httponly' => true, // not readable by JavaScript
'samesite' => 'Lax',
]);
ini_set('session.use_strict_mode', '1'); // reject unknown session IDs
session_start();
| Setting | Why |
|---|---|
secure | The session ID must never travel over plain HTTP |
httponly | XSS cannot steal the session ID through document.cookie |
samesite=Lax | Reduces CSRF risk |
session.use_strict_mode=1 | PHP refuses to accept session IDs it did not create, which blocks a form of session fixation |
session_regenerate_id(true) after login and privilege changes | New ID whenever the user's access level changes |
These can also be set globally in php.ini (session.cookie_secure, session.cookie_httponly, session.cookie_samesite).
Session Lifetime and Storage
- Expiry: Sessions expire after a period of inactivity determined by
session.gc_maxlifetime(default 1440 seconds \= 24 minutes). PHP's garbage collector removes old session files probabilistically, so expiry is not exact. For a strict timeout, store a "last activity" timestamp in the session and check it on each request. - Storage: By default, sessions are stored as files on the server. Applications running on several servers use shared storage such as Redis, Memcached, or a database, so that any server can load any session.
- Locking: With file storage, PHP locks the session for the duration of a request. A slow request blocks other requests from the same user (for example, parallel AJAX calls). Call
session_write_close()as soon as you have finished writing session data in long-running scripts.
<?php
session_start();
$userId = $_SESSION['user_id'] ?? null;
session_write_close(); // release the lock; $_SESSION can still be read but changes are not saved
// ... long-running work, such as generating a report ...
Common Mistakes
Calling session_start() After Output
Effect: Warning, and the session cookie cannot be sent.
Fix: Start the session before any output.
Forgetting session_start() on a Page
Effect: $_SESSION is empty, so the user appears logged out.
Fix: Start the session on every page that uses it (preferably in a shared bootstrap file).
Not Regenerating the ID After Login
Effect: Session fixation attacks.
Fix: session_regenerate_id(true) immediately after successful login.
Storing Sensitive or Large Data
Effect: Passwords in session files, slow requests with huge sessions.
Fix: Store identifiers (user ID), not secrets; load other data from the database when needed.
Incomplete Logout
Effect: The session cookie or data remains.
Fix: Clear $_SESSION, delete the cookie, and call session_destroy().
Trusting Session Data Forever
Effect: Users keep permissions that were revoked in the database.
Fix: Re-check important permissions against the database, or invalidate sessions when roles change.
Best Practices
- Start sessions early, in one central place, with secure cookie parameters.
- Store minimal data — usually the user's ID — and look up the rest.
- Regenerate the session ID on login and whenever privileges change.
- Implement logout completely and protect it with POST and a CSRF token.
- Use shared session storage (Redis or a database) when running multiple servers, and release session locks early in long requests.
Official reference: https\://www.php.net/manual/en/reserved.variables.session.php