Logout Flow
How logout works and session destruction
What Logout Actually Does
Logging out is the opposite of logging in. It means:
- Remove the user from the session
- Destroy the session data on the server
- Delete the session cookie from the browser
Let's see how DALT.PHP handles this.
The Logout Controller
// .dalt/stubs/auth/Http/controllers/session/destroy.php
$auth = new Authenticator();
$auth->logout();
redirect('/');That's it! Just three lines:
- Create an
Authenticatorinstance - Call
logout() - Redirect to the homepage
The real work happens inside the logout() method.
The Logout Method
// framework/Core/Authenticator.php
public function logout()
{
Session::destroy();
}This calls Session::destroy(), which does the heavy lifting.
Session::destroy() - The Complete Cleanup
// framework/Core/Session.php
public static function destroy()
{
// 1. Get the session cookie name
$cookieName = session_name();
// 2. Clear all session data
static::flush();
// 3. Destroy the session on the server
if (session_status() === PHP_SESSION_ACTIVE) {
session_destroy();
}
// 4. Delete the cookie from the browser
$params = session_get_cookie_params();
setcookie(
$cookieName,
'',
time() - 3600,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']
);
}Let's break down each step.
Step-by-Step Breakdown
Step 1: Get the Session Cookie Name
$cookieName = session_name();Remember from the Entry Point section, we set a custom session name:
session_name('daltphp_' . substr(md5(BASE_PATH), 0, 8));This might be something like daltphp_ab12cd34.
We need this name to delete the cookie later.
Step 2: Clear All Session Data
static::flush();The flush() method clears everything from $_SESSION:
public static function flush()
{
$_SESSION = [];
}This removes:
- The logged-in user info
- Any flash data
- Any other data stored in the session
Step 3: Destroy the Session on the Server
if (session_status() === PHP_SESSION_ACTIVE) {
session_destroy();
}What is session_status()?
It returns the current session state:
PHP_SESSION_DISABLED- Sessions are disabledPHP_SESSION_NONE- Sessions are enabled but not startedPHP_SESSION_ACTIVE- A session is currently active
We only call session_destroy() if a session is active.
What does session_destroy() do?
It deletes the session data file on the server. PHP stores session data in files (by default in /tmp or a configured directory).
Each session has a file like: sess_abc123def456...
session_destroy() deletes that file.
Step 4: Delete the Cookie from the Browser
$params = session_get_cookie_params();
setcookie(
$cookieName,
'',
time() - 3600,
$params['path'],
$params['domain'],
$params['secure'],
$params['httponly']
);Why do we need to delete the cookie?
Even though we destroyed the session on the server, the browser still has the session cookie. If we don't delete it:
- The browser keeps sending the old session ID
- The server creates a new empty session with that ID
- This can cause confusion
How do we delete a cookie?
You can't actually "delete" a cookie. Instead, you set it with an expiry time in the past:
time() - 3600 // 1 hour agoWhen the browser sees a cookie with an expired time, it deletes it.
What are the cookie parameters?
$params = session_get_cookie_params();This returns an array with:
path- Which URL paths can access this cookie (usually/)domain- Which domain can access this cookiesecure- Only send over HTTPS?httponly- Prevent JavaScript from accessing this cookie?
We use the same parameters that were used to create the cookie, so we're deleting the exact same cookie.
The Complete Logout Flow
User clicks "Logout"
↓
POST /logout (or GET, depending on route)
↓
Logout controller
↓
Authenticator::logout()
↓
Session::destroy()
↓
Get session cookie name
↓
Clear $_SESSION array
↓
Destroy session file on server
↓
Delete session cookie from browser
↓
Redirect to /
↓
User is now logged outELI5: The Logout Flow
Imagine you're leaving a hotel:
- You go to the front desk (click logout)
- They take your room key (clear session data)
- They shred your registration (destroy session file)
- They deactivate your key card (delete cookie)
- You leave the hotel (redirect to homepage)
Now if you try to use that key card again, it won't work. You'd need to check in again (log in).
Why All These Steps?
You might wonder: "Why not just clear $_SESSION? Why destroy the session and delete the cookie?"
Just Clearing $_SESSION
// ❌ INCOMPLETE
$_SESSION = [];Problems:
- Session file still exists on server (wasted space)
- Cookie still exists in browser
- Old session ID could be reused
Just Calling session_destroy()
// ❌ INCOMPLETE
session_destroy();Problems:
$_SESSIONstill has data in memory (for this request)- Cookie still exists in browser
- Next request creates a new session with the same ID
Complete Cleanup
// ✅ PROPER LOGOUT
$_SESSION = []; // Clear memory
session_destroy(); // Delete server file
setcookie(...); // Delete browser cookieThis ensures:
- No data leaks in the current request
- No wasted space on the server
- No confusion from old cookies
Security Considerations
Why HttpOnly Matters
$params['httponly']When a cookie is marked httponly, JavaScript cannot access it:
// This returns nothing if httponly is true
document.cookieThis protects against XSS attacks where malicious JavaScript tries to steal your session cookie.
Why Secure Matters
$params['secure']When a cookie is marked secure, it's only sent over HTTPS, not HTTP.
This prevents session hijacking on unsecured networks (like public WiFi).
Session Fixation Protection
Remember from the login page, we call:
session_regenerate_id(true);This changes the session ID after login. Combined with proper logout, this prevents:
- Attacker forces you to use their session ID
- You log in
- Attacker uses that session ID to access your account
By regenerating on login and destroying on logout, we close this attack vector.
Common Logout Patterns
Logout Link in Navigation
<!-- resources/views/layouts/nav.php -->
<?php if (isset($_SESSION['user'])) : ?>
<span>Welcome, <?= htmlspecialchars($_SESSION['user']['email']) ?></span>
<form method="POST" action="/logout" style="display: inline;">
<input type="hidden" name="csrf_token" value="<?= csrf_token() ?>">
<button type="submit">Logout</button>
</form>
<?php else : ?>
<a href="/login">Login</a>
<a href="/register">Register</a>
<?php endif; ?>Why a form instead of a link?
// ❌ BAD - Vulnerable to CSRF
<a href="/logout">Logout</a>
// ✅ GOOD - Protected by CSRF token
<form method="POST" action="/logout">
<input type="hidden" name="csrf_token" value="<?= csrf_token() ?>">
<button type="submit">Logout</button>
</form>If logout is a GET request (just a link), an attacker could:
- Send you an email with
<img src="https://yoursite.com/logout"> - When you open the email, the image loads
- You're logged out without knowing
Using POST with CSRF protection prevents this.
Logout Route
// routes/web.php
$router->post('/logout', 'session/destroy.php');Notice it's post, not get, for the security reason above.
What Happens After Logout?
After logout completes and you're redirected to /:
$_SESSIONis empty- The session file on the server is deleted
- The browser has no session cookie
- Any page protected by
Authmiddleware will redirect you to/(or/login) - You're treated as a guest user
If you want to access protected pages again, you need to log in.
What's Good Here
- Complete cleanup: memory, server, and browser
- Reuses cookie parameters (ensures we delete the right cookie)
- Checks if session is active before destroying
- Simple API: just call
logout()
Cookie Note
DALT deletes the session cookie using the modern setcookie(..., $options) array syntax (PHP 7.3+), and includes a SameSite value (defaults to Lax).
No Logout Confirmation
Some apps show:
- "Are you sure you want to log out?"
- Or a success message: "You've been logged out"
This is a UX choice, not a security issue.
No "Logout from All Devices"
If you log in from multiple devices, logging out from one doesn't log you out from others.
To implement this, you'd need:
- Store session IDs in the database per user
- On "logout all", delete all session files for that user
- More complex, but useful for security-sensitive apps
Testing Logout
Here's how to verify logout works:
- Log in to your app
- Check that
$_SESSION['user']exists (you canvar_dump()it) - Check that you can access protected pages
- Click logout
- Check that
$_SESSIONis empty - Check that you can't access protected pages anymore
- Check that the session cookie is gone (in browser dev tools → Application → Cookies)
Common Mistakes to Avoid
Forgetting to Delete the Cookie
// ❌ INCOMPLETE
public static function destroy()
{
$_SESSION = [];
session_destroy();
// Missing: setcookie() to delete the cookie
}The browser keeps sending the old session ID, causing confusion.
Using GET for Logout
// ❌ VULNERABLE TO CSRF
$router->get('/logout', 'session/destroy.php');An attacker can log you out by tricking you into visiting a URL.
Not Checking Session Status
// ❌ CAN CAUSE WARNINGS
session_destroy();If no session is active, this causes a PHP warning. Always check first:
// ✅ SAFE
if (session_status() === PHP_SESSION_ACTIVE) {
session_destroy();
}Redirecting Before Destroying Session
// ❌ WRONG ORDER
redirect('/');
Session::destroy(); // This never runs!redirect() calls exit(), so code after it doesn't execute. Always destroy the session first.