PHP: Sessions and Login System
Last updated: 2026-08-26
Cookies keep data in the user's browser; Sessions keep it on the server. Login state, shopping carts, permission flags — any data you don't want the user touching belongs in a session. By the end of this lesson, you'll have built your first login system.
1. What Is a Session?
A session is a private storage area on the server created for each user. The browser only holds a cookie with the Session ID; all the real data stays server-side:
Cookie: Session:
Only holds the Session ID Stores the actual data (username, role, cart...)
┌──────────────┐ ┌──────────────────────┐
│ PHPSESSID │ ──match──→ │ user: John │
│ abc123 │ │ login: true │
└──────────────┘ │ cart: [3 items] │
In the browser │ role: admin │
└──────────────────────┘
On the server
2. session_start() — Starting a Session
You must call session_start() before using any session functionality. It does two things:
- If the browser sends a Session ID cookie → load the corresponding server data
- If no cookie is found → create a new session, generate a new ID, and send a cookie
<?php
// session_start() must come before any HTML output
session_start();
// Store data
$_SESSION['username'] = "John";
$_SESSION['login_time'] = date("Y-m-d H:i:s");
// Read data
echo "Welcome, {$_SESSION['username']}!";
echo "Logged in at: {$_SESSION['login_time']}";
?>
session_start() must be called before any output — echo, raw HTML, even a stray space before the <?php tag. Otherwise you'll get the dreaded headers already sent error. Put it at the very top of your file.
3. $_SESSION — Use It Like Any Array
<?php
session_start();
// Store any PHP data type
$_SESSION['user'] = [
'id' => 101,
'name' => 'John',
'role' => 'member'
];
$_SESSION['cart'] = [
['name' => 'PHP Tutorial', 'price' => 39.90, 'qty' => 2],
['name' => 'MySQL Basics', 'price' => 29.90, 'qty' => 1],
];
// Read
echo $_SESSION['user']['name']; // John
// Remove a single session variable
unset($_SESSION['cart']);
// Update
$_SESSION['user']['role'] = 'admin';
?>
▶ Example: A Session-Powered Shopping Cart
Output:
<h3>Your Cart</h3>
{item1} × {1}<br>
<?php
session_start();
// Initialize the cart
if (!isset($_SESSION['cart'])) {
$_SESSION['cart'] = [];
}
// Add an item
if (isset($_POST['add'])) {
$item = $_POST['add'];
$_SESSION['cart'][$item] = ($_SESSION['cart'][$item] ?? 0) + 1;
}
echo "<h3>Your Cart</h3>";
foreach ($_SESSION['cart'] as $item => $qty) {
echo "{$item} × {$qty}<br>";
}
?>
<form method='POST'>
<button name='add' value='PHP Tutorial'>Add PHP Tutorial</button>
<button name='add' value='MySQL Basics'>Add MySQL Basics</button>
</form>
Output:
Output displayed
4. Building a Login System
▶ Example: Complete Login System
Output:
(no visible output)
<?php
// login.php — Login System
session_start();
// Predefined test users (in a real project, these live in a database)
$users = [
'admin' => '123456',
'john' => 'password123',
];
$error = '';
// Already logged in? Redirect to profile
if (isset($_SESSION['user'])) {
header("Location: profile.php");
exit;
}
// Handle login request
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username'] ?? '');
$password = $_POST['password'] ?? '';
if (isset($users[$username]) && $users[$username] === $password) {
// Credentials verified — store user in session
$_SESSION['user'] = [
'username' => $username,
'login_time' => date("Y-m-d H:i:s"),
'last_active' => time(),
];
// Redirect to profile page
header("Location: profile.php");
exit;
} else {
$error = "Invalid username or password";
}
}
?>
<h3>Login</h3>
<?php if ($error): ?>
<p style="color:red"><?= htmlspecialchars($error) ?></p>
<?php endif; ?>
<form method="POST" action="">
<p>
<label>Username:</label>
<input type="text" name="username" required>
</p>
<p>
<label>Password:</label>
<input type="password" name="password" required>
</p>
<button type="submit">Log In</button>
</form>
<p style="color:#888">Test account: admin / 123456</p>
Output:
Username: admin
Logged in at: 2025-01-15 10:30:00
<?php
// profile.php — Profile Page (requires login)
session_start();
// Not logged in? Redirect to login
if (!isset($_SESSION['user'])) {
header("Location: login.php");
exit;
}
$user = $_SESSION['user'];
?>
<h2>Profile</h2>
<p>Username: <?= htmlspecialchars($user['username']) ?></p>
<p>Logged in at: <?= $user['login_time'] ?></p>
<a href="logout.php">Log Out</a>
(1) Logging Out
<?php
// logout.php
session_start();
// Option 1: Clear only the user data
unset($_SESSION['user']);
// Option 2: Wipe the entire session
session_unset(); // Clear the $_SESSION array
session_destroy(); // Delete the server-side session file
// Redirect to login
header("Location: login.php");
exit;
?>
| Operation | Function | What It Does |
|---|---|---|
| Remove single item | unset($_SESSION['key']) |
Deletes a specific key |
| Clear all variables | session_unset() |
Empties $_SESSION |
| Delete session file | session_destroy() |
Removes server-side session data |
| Delete cookie | setcookie(session_name(), '', time()-3600, '/') |
Removes the PHPSESSID cookie from the browser |
session_unset() → session_destroy() → delete the PHPSESSID cookie → redirect to the login page.
5. The Session Lifecycle
User visits for the first time → PHP creates a session file (e.g., sess_abc123)
↓
Data is written to $_SESSION
↓
Cookie sent: PHPSESSID=abc123 to the browser
↓
Next visit → Browser sends PHPSESSID=abc123
↓
PHP finds sess_abc123, loads data into $_SESSION
↓
... User leaves (closes browser or is idle for a long time) ...
↓
Garbage collection → Expired session files are deleted
Sessions expire by default after 24 minutes (session.gc_maxlifetime = 1440 seconds in php.ini). The clock starts from the file's last modification time, not the user's last activity.
6. Session Security Configuration
<?php
// Configure before session_start()
ini_set('session.cookie_httponly', 1); // JS can't read the session cookie (XSS protection)
ini_set('session.cookie_secure', 1); // HTTPS only (required for production)
ini_set('session.cookie_samesite', 'Lax'); // CSRF mitigation
ini_set('session.use_strict_mode', 1); // Reject uninitialized session IDs
session_start();
?>
session.use_strict_mode = 1 prevents attackers from fabricating a Session ID. If an attacker invents a non-existent ID, PHP rejects it and creates a fresh session instead.
7. Cookies vs Sessions at a Glance
| Cookie | Session | |
|---|---|---|
| Data location | User's browser | Server |
| Security | Low (user can view/edit) | High (user never sees the data) |
| Capacity | 4KB | Unlimited (limited only by server disk) |
| Performance | Sent with every request | No bandwidth cost (server-side lookup) |
| Dependency | Standalone | Depends on cookies (for Session ID) |
| Best for | Preferences, light state | Login, permissions, shopping carts |
▶ Example: Session Destroy and Regenerate ID
Output:
Page views this session: " . value['views'] . "<br>
Session destroyed and ID regenerated
<?php
session_start();
$_SESSION['views'] = ($_SESSION['views'] ?? 0) + 1;
echo "Page views this session: " . $_SESSION['views'] . "<br>";
if (isset($_GET['reset'])) {
session_unset();
session_destroy();
session_start();
session_regenerate_id(true);
echo "Session destroyed and ID regenerated";
}
?>
<a href="?reset=1">Reset Session</a>
Output:
Output displayed
❓ FAQ
$_SESSION always belongs to the current user — there's no cross-contamination.PHPSESSID=abc123), but this is very unsafe — the Session ID ends up in browser history, bookmarks, and server logs.📖 Summary
- Session data lives on the server; only the Session ID travels in a cookie
session_start()must be called before any output$_SESSIONbehaves like any other associative array- Login flow: validate credentials →
$_SESSION['user'] = ...→ redirect - Logout:
session_unset()+session_destroy()+ redirect - Security: httponly + secure + samesite + strict_mode
- Sessions are ideal for login state, shopping carts, and permission data
📝 Exercises
- Deploy the complete login system from the examples above (login.php → profile.php → logout.php).
- Add a "Change Password" feature to the profile page (validate against the predefined users array).
- Implement simple role-based access: only users with
role === 'admin'can see an "Admin Panel" link on the page.