How Sessions Work
- You call
session_start(). PHP looks for aPHPSESSIDcookie. - If missing, PHP generates a unique session ID and sets the cookie.
- PHP loads the session's stored data from disk into
$_SESSION. - You read/write
$_SESSIONfreely during the request. - At script end, PHP writes
$_SESSIONback to disk.
Starting a Session
<?php
// MUST come before any output (HTML, echo, even whitespace before <?php)
session_start();
// Or with options (PHP 7+)
session_start([
"cookie_lifetime" => 0, // until browser close
"cookie_secure" => true, // HTTPS only
"cookie_httponly" => true, // not accessible to JavaScript
"cookie_samesite" => "Lax", // CSRF protection
"use_strict_mode" => true, // reject unknown session IDs
]);
Storing & Reading Data
<?php
session_start();
// Write
$_SESSION["user_id"] = 42;
$_SESSION["preferences"] = ["theme" => "dark", "lang" => "en"];
// Read (with safe defaults)
$userId = $_SESSION["user_id"] ?? null;
$theme = $_SESSION["preferences"]["theme"] ?? "light";
// Check
if (isset($_SESSION["user_id"])) {
echo "Logged in as $userId";
}
// Remove a single key
unset($_SESSION["preferences"]);
// Clear all data (but keep the session)
$_SESSION = [];
Login Example
<?php
session_start();
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$email = trim($_POST["email"] ?? "");
$password = $_POST["password"] ?? "";
$user = $db->fetchUserByEmail($email);
if ($user && password_verify($password, $user["password_hash"])) {
// PREVENT SESSION FIXATION - regenerate the ID
session_regenerate_id(true);
$_SESSION["user_id"] = $user["id"];
$_SESSION["user_name"] = $user["name"];
$_SESSION["login_at"] = time();
header("Location: /dashboard");
exit;
}
$error = "Invalid credentials";
}
Logout & Destroying Sessions
<?php
session_start();
// 1. Empty the data
$_SESSION = [];
// 2. Tell the browser to forget the cookie
if (ini_get("session.use_cookies")) {
$params = session_get_cookie_params();
setcookie(session_name(), "", time() - 42000,
$params["path"], $params["domain"],
$params["secure"], $params["httponly"]);
}
// 3. Destroy the server-side data
session_destroy();
header("Location: /login");
exit;
Cookies Direct
<?php
// Set a cookie - MUST be before any output
setcookie("theme", "dark", [
"expires" => time() + 86400 * 30, // 30 days
"path" => "/",
"domain" => "example.com",
"secure" => true, // HTTPS only
"httponly" => true, // no JS access (PHP 7.3+ options)
"samesite" => "Strict",
]);
// Read (next request)
$theme = $_COOKIE["theme"] ?? "light";
// Delete
setcookie("theme", "", time() - 3600, "/");
Secure Session Config (php.ini)
; Don't accept session IDs from the URL
session.use_only_cookies = 1
session.use_trans_sid = 0
; Use HTTPS-only cookies
session.cookie_secure = 1
session.cookie_httponly = 1
session.cookie_samesite = "Lax"
; Reject unknown session IDs (prevents fixation)
session.use_strict_mode = 1
; Use a stronger hash for session IDs
session.sid_length = 48
session.sid_bits_per_character = 6
; Custom save path on fast disk
session.save_path = "/var/lib/php/sessions"
session.gc_maxlifetime = 1440
Call session_regenerate_id(true) after login, logout, role change, or any sensitive action. Otherwise an attacker who learned the pre-login session ID can hijack the post-login session.
Flash Messages
One-time messages between requests (e.g., "Saved successfully" after a redirect):
<?php
session_start();
function flash(string $key, ?string $msg = null): ?string {
if ($msg !== null) {
$_SESSION["_flash"][$key] = $msg;
return null;
}
$val = $_SESSION["_flash"][$key] ?? null;
unset($_SESSION["_flash"][$key]);
return $val;
}
// On save handler
flash("success", "Post saved!");
header("Location: /posts");
// On the destination page
if ($msg = flash("success")) {
echo "<div class=\"alert\">" . htmlspecialchars($msg) . "</div>";
}