Topic 40 of 56 · Full Stack Advanced

Topic 7: Sessions, Login Page, Logout (Session Destroy, Timeout, Cookies Introduction, Page Redirect)

Lesson TL;DRTopic 7: Sessions, Login Page, Logout (Session Destroy, Timeout, Cookies Introduction, Page Redirect) 📖 5 min read · 🎯 intermediate · 🧭 Prerequisites: multiprocessinginnodejs, reactlists Why this m...
5 min read·intermediate·php · sessions · authentication · cookies

Topic 7: Sessions, Login Page, Logout (Session Destroy, Timeout, Cookies Introduction, Page Redirect)

📖 5 min read · 🎯 intermediate · 🧭 Prerequisites: multi-processing-in-nodejs, react-lists

Why this matters

Up until now, your PHP pages have no memory — every request starts fresh, with no idea who's on the other side. That's fine for a blog post, but the moment you add a login page, you need the server to remember: "Yes, this person already signed in." That's exactly what sessions give you. In this lesson we'll build a real login flow in PHP — session start, session destroy on logout, automatic timeout when someone walks away, and a first look at cookies for returning users.

What You'll Learn

  • Create a complete PHP login page that authenticates users against a MySQL database
  • Start, read, and destroy PHP sessions to track who is logged in
  • Implement a 30-minute session timeout for automatic logout
  • Set and read cookies to remember returning users across browser restarts
  • Redirect users with header("Location: ...") based on authentication state

The Analogy

Think of your PHP application as a city museum with restricted galleries. The login page is the front desk — a visitor presents their ID (username and password), the clerk checks the registry (the database), and if everything matches, they hand over a wristband ($_SESSION). Every room the visitor enters checks for that wristband. If they've been idle for 30 minutes, a security guard quietly removes it and asks them to check back in. Cookies are the annual membership card tucked in a wallet: even after the visitor goes home and returns the next day, the front desk recognizes their face and fastens the wristband again without a full interrogation.

Chapter 1: Database Setup

Before writing a single line of PHP, you need a users table in MySQL. Store hashed passwords — never plain text.

CREATE DATABASE user_management;
USE user_management;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL
);

INSERT INTO users (username, password) VALUES
('admin', '$2y$10$WzH/q9TvkhO92eB.k7B8ue4jeVRRTY7jbWnce7xxEr5EIKD1S/WPC');
-- plain-text equivalent: 'password'
-- generated with: password_hash('password', PASSWORD_BCRYPT)

The password column is 255 characters to accommodate the full bcrypt hash output. The UNIQUE constraint on username prevents duplicate accounts at the database level.

Chapter 2: Creating the Login Page

Create login.php. This file does two jobs: it renders the HTML form on a GET request and processes credentials on a POST request.

<?php
session_start();

$servername = "localhost";
$username   = "root";
$password   = "";
$dbname     = "user_management";

$conn = new mysqli($servername, $username, $password, $dbname);

if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    $username = $_POST['username'];
    $password = $_POST['password'];

    $sql    = "SELECT * FROM users WHERE username = '$username'";
    $result = $conn->query($sql);

    if ($result->num_rows > 0) {
        $row = $result->fetch_assoc();
        if (password_verify($password, $row['password'])) {
            $_SESSION['username'] = $username;
            header("Location: welcome.php");
            exit();
        } else {
            echo "Invalid password.";
        }
    } else {
        echo "No user found.";
    }
}
?>
<!DOCTYPE html>
<html>
<head>
    <title>Login</title>
</head>
<body>
    <form method="post" action="">
        Username: <input type="text"     name="username" required><br>
        Password: <input type="password" name="password" required><br>
        <button type="submit">Login</button>
    </form>
</body>
</html>

Key points:

  • session_start() must be called before any output — it initialises (or resumes) the session cookie exchange with the browser.
  • password_verify($password, $row['password']) compares the submitted plain-text password against the stored bcrypt hash. Never compare plain text to plain text.
  • header("Location: welcome.php") followed immediately by exit() performs the redirect and stops further execution. The exit() is non-negotiable — without it, the script continues running after sending the header.

Chapter 3: Creating the Welcome Page

Create welcome.php. This page is the "restricted gallery" — only visitors wearing the session wristband may enter.

<?php
session_start();

if (!isset($_SESSION['username'])) {
    header("Location: login.php");
    exit();
}

echo "Welcome, " . $_SESSION['username'];
?>
<!DOCTYPE html>
<html>
<head>
    <title>Welcome</title>
</head>
<body>
    <p>You are logged in.</p>
    <a href="logout.php">Logout</a>
</body>
</html>

The guard check — if (!isset($_SESSION['username'])) — redirects unauthenticated visitors back to the login page before they see any protected content.

Chapter 4: Creating the Logout Page

Create logout.php. Logging out means both clearing the session data and destroying the session itself.

<?php
session_start();
session_unset();    // clear all session variables
session_destroy();  // destroy the session on the server
header("Location: login.php");
exit();
?>
  • session_unset() empties the $_SESSION superglobal array.
  • session_destroy() deletes the session file (or DB row) on the server, invalidating the session ID entirely.
  • Both steps together ensure no residual data leaks.

Chapter 5: Session Timeout and Cookies

Session Timeout

Add a 30-minute inactivity timeout to both login.php (after authentication) and welcome.php (at the top of every page load). Place this block immediately after session_start():

<?php
// login.php and welcome.php — add this right after session_start()
session_start();

$timeout_duration = 1800; // 30 minutes in seconds

if (isset($_SESSION['LAST_ACTIVITY']) &&
    (time() - $_SESSION['LAST_ACTIVITY']) > $timeout_duration) {
    session_unset();
    session_destroy();
    header("Location: login.php");
    exit();
}

$_SESSION['LAST_ACTIVITY'] = time(); // refresh timestamp on every page load

Every page load stamps the current Unix timestamp into $_SESSION['LAST_ACTIVITY']. On the next request, if more than 1800 seconds have elapsed since that stamp, the session is destroyed and the user is bounced back to the login page.

Using Cookies

Cookies persist across browser sessions (unlike the in-memory PHP session cookie). Use them to remember a returning user's identity so you can restore their session without forcing a full re-login.

In login.php — set the cookie on successful login:

<?php
// Inside the successful password_verify() branch in login.php
if (password_verify($password, $row['password'])) {
    $_SESSION['username'] = $username;

    // Set a cookie that expires in 30 days
    setcookie("username", $username, time() + (86400 * 30), "/");

    header("Location: welcome.php");
    exit();
}

setcookie parameters:

  • "username" — cookie name
  • $username — value stored in the cookie
  • time() + (86400 * 30) — expiry: current time plus 30 days (86 400 seconds × 30)
  • "/" — path scope: the cookie is sent for every URL on this domain

In welcome.php — restore session from cookie if session is absent:

<?php
session_start();

// Restore session from cookie if the session variable is missing
if (isset($_COOKIE['username']) && !isset($_SESSION['username'])) {
    $_SESSION['username'] = $_COOKIE['username'];
}

if (!isset($_SESSION['username'])) {
    header("Location: login.php");
    exit();
}

This means a user who closes and reopens the browser (clearing the in-memory session) will have their session silently restored from the cookie during the next visit — no re-login needed until the 30-day cookie expires.

sequenceDiagram
    participant Browser
    participant login.php
    participant DB
    participant welcome.php
    participant logout.php

    Browser->>login.php: POST username + password
    login.php->>DB: SELECT * FROM users WHERE username = ?
    DB-->>login.php: Row with hashed password
    login.php->>login.php: password_verify()
    login.php->>Browser: Set-Cookie: username=admin (30d) + redirect to welcome.php
    Browser->>welcome.php: GET (with session cookie + username cookie)
    welcome.php->>welcome.php: Check $_SESSION['username']
    welcome.php-->>Browser: 200 Welcome page
    Browser->>logout.php: GET /logout.php
    logout.php->>logout.php: session_unset() + session_destroy()
    logout.php->>Browser: redirect to login.php

🧪 Try It Yourself

Task: Wire up the full login/logout flow locally.

  1. Create the user_management database and users table using the SQL from Chapter 1.
  2. Create login.php, welcome.php, and logout.php from the examples above.
  3. Start a local PHP server in the same directory:
php -S localhost:8000
  1. Open http://localhost:8000/login.php in your browser and log in with admin / password.

Success criterion: After login you should land on http://localhost:8000/welcome.php and see "Welcome, admin". Clicking "Logout" should redirect you back to the login page and make welcome.php inaccessible (redirecting you back to login if you try to visit it directly).

Bonus: Change $timeout_duration to 10 (10 seconds), wait 11 seconds on welcome.php, then refresh — you should be redirected to login.php automatically.

🔍 Checkpoint Quiz

Q1. Why must session_start() be called before any HTML output in a PHP file?

Q2. Given this snippet, what happens when a user visits welcome.php without a valid session?

<?php
session_start();
if (!isset($_SESSION['username'])) {
    header("Location: login.php");
    exit();
}
echo "Welcome, " . $_SESSION['username'];

A) It shows "Welcome, " with an empty string
B) It redirects to login.php and stops execution
C) It throws a PHP fatal error
D) It shows a blank page

Q3. What is the difference between session_unset() and session_destroy()?

Q4. You want a "remember me" cookie to last exactly 7 days. Which setcookie call is correct?

A) setcookie("user", $name, time() + 604800, "/");
B) setcookie("user", $name, 604800, "/");
C) setcookie("user", $name, time() + 7, "/");
D) setcookie("user", $name, time() - 604800, "/");

A1. session_start() sends HTTP headers (specifically the Set-Cookie header for the session ID). HTTP headers must be sent before any body content — once the browser receives output, headers are already flushed and can no longer be modified.

A2. B — !isset($_SESSION['username']) is true, so header("Location: login.php") fires and exit() halts the script before echo is ever reached.

A3. session_unset() clears the contents of the $_SESSION array (variables gone, session still exists on the server). session_destroy() deletes the entire session record from the server, invalidating the session ID. For a clean logout you call both: unset the data first, then destroy the container.

A4. A — time() + 604800 is the correct Unix timestamp 7 days from now (7 × 24 × 60 × 60 = 604 800 seconds). Option B passes a raw offset instead of an absolute timestamp. Option C adds only 7 seconds. Option D subtracts time, immediately expiring the cookie.

🪞 Recap

  • Call session_start() at the top of every PHP file that reads or writes session data — before any output.
  • Use password_hash() at registration and password_verify() at login; never store or compare plain-text passwords.
  • Protect pages by checking $_SESSION['username'] and redirecting with header("Location: ...") + exit() when the check fails.
  • Implement session timeout by storing $_SESSION['LAST_ACTIVITY'] and comparing against time() on each page load.
  • Use setcookie() to persist a "remember me" identity across browser restarts; restore the session from $_COOKIE in welcome.php when the session variable is missing.

📚 Further Reading

Like this topic? It’s one of 56 in Full Stack Advanced.

Block your seat for ₹2,500 and join the next cohort.