PHP Login System with Sessions (Secure, Step by Step)

A login system is part of almost every PHP project, and getting it right means understanding two things: sessions (how PHP remembers a logged-in user across pages) and password hashing (how you store passwords safely). This guide builds a complete, secure login system step by step, registration, login, protecting pages, and logout, with working code and the security done properly.

This tutorial uses PDO for the database and PHP’s modern password_hash and password_verify functions. If you are new to connecting PHP to a database, read the PDO connection basics first, then come back, this guide assumes you can already connect to MySQL.
What you’ll build
  1. How a login system actually works
  2. The users table
  3. Registration (with password hashing)
  4. Login (verifying the password and starting a session)
  5. Protecting a page (checking the session)
  6. Logout (destroying the session)
  7. Security essentials

1. How a login system actually works

A login system has a simple flow once you see it. When a user registers, you store their details with a securely hashed password, never the plain password. When they log in, you look up their record, verify the typed password against the stored hash, and if it matches, you start a session that remembers them. Every protected page then checks that session before showing anything. When they log out, you destroy the session. That is the entire system: hash on register, verify on login, remember with a session, check on every protected page, destroy on logout.

2. The users table

Start with a simple table to hold users:

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

Note the password column is VARCHAR(255). This matters: a hashed password is much longer than the original, and 255 characters leaves room for it. Making this column too short is a common beginner mistake that quietly breaks logins.

3. Registration (with password hashing)

register.php

When a user registers, hash the password with password_hash before storing it. This turns the password into a secure, irreversible hash.

// assume $pdo is your PDO connection
$username = $_POST['username'];
$password = $_POST['password'];

// hash the password (never store the plain text)
$hash = password_hash($password, PASSWORD_DEFAULT);

try {
  $stmt = $pdo->prepare(
    "INSERT INTO users (username, password) VALUES (?, ?)"
  );
  $stmt->execute([$username, $hash]);
  echo "Registration successful. You can now log in.";
} catch (PDOException $e) {
  // likely a duplicate username (UNIQUE constraint)
  echo "That username is already taken.";
}
password_hash with PASSWORD_DEFAULT automatically uses a strong, modern hashing algorithm and handles the technical details (like salting) for you. You never need to write your own password encryption, this is the correct, secure way.

4. Login (verifying the password and starting a session)

login.php

On login, look up the user by username, then use password_verify to check the typed password against the stored hash. If it matches, start a session and store something that marks the user as logged in.

session_start();

$username = $_POST['username'];
$password = $_POST['password'];

$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();

if ($user && password_verify($password, $user['password'])) {
  // correct password: log them in
  $_SESSION['user_id'] = $user['id'];
  $_SESSION['username'] = $user['username'];
  header("Location: dashboard.php");
  exit;
} else {
  echo "Invalid username or password.";
}
Always give the same error for a wrong username and a wrong password (“Invalid username or password”). Telling the user which one was wrong helps attackers figure out which usernames exist. One generic message is safer.

5. Protecting a page (checking the session)

dashboard.php

Any page that should only be visible to logged-in users must check the session at the very top, before outputting anything. If there is no logged-in user, send them to the login page.

session_start();

// if not logged in, redirect to login
if (!isset($_SESSION['user_id'])) {
  header("Location: login.php");
  exit;
}

// safe to show protected content
echo "Welcome, " . htmlspecialchars($_SESSION['username']);

This check is what actually protects your pages. Put it at the top of every page that requires login. Notice htmlspecialchars when printing the username, that prevents anyone’s stored name from injecting HTML or scripts into your page.

6. Logout (destroying the session)

logout.php

Logging out means ending the session completely:

session_start();
session_unset();   // clear session variables
session_destroy(); // destroy the session
header("Location: login.php");
exit;

7. Security essentials

A login system is a security feature, so these are not optional extras, they are the point:

  • Always hash passwords with password_hash, and verify with password_verify. Never store or compare plain text passwords.
  • Always use prepared statements (as shown) so login fields cannot be used for SQL injection.
  • Use one generic login error so you do not reveal which usernames exist.
  • Escape output with htmlspecialchars when displaying user-provided data.
  • Check the session at the top of every protected page, before any output.
This login pattern, hash, verify, session, protect, logout, is exactly how the projects on CodeZips handle their admin and user logins. Browse the ready-to-run PHP projects with full source code to see a complete login system working inside a real application, alongside the rest of the project.

Frequently asked questions

Why use sessions for login?

HTTP is stateless, meaning each page request is independent and the server does not naturally remember you between pages. A session stores a small piece of information on the server that identifies the logged-in user across requests, which is what keeps you logged in as you move around the site.

Is password_hash secure enough?

Yes. password_hash with PASSWORD_DEFAULT uses a strong, modern, salted hashing algorithm and is the officially recommended way to handle passwords in PHP. You should not write your own password encryption.

Why is my session not working?

The most common cause is forgetting to call session_start() at the very top of every page that uses the session, before any HTML or output. It must run before anything is sent to the browser.

Why must the password column be VARCHAR(255)?

A hashed password is much longer than the original text. If the column is too short, the hash gets cut off when stored, and logins silently fail. 255 characters gives plenty of room.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top