Login with sessions
| password_hash and verify | |
| A login sketch | |
| Guard pages | |
| Logout |
password_hash and verify
Passwords are never stored as plain text. A hash is a one-way fingerprint: easy to compute, practically impossible to reverse. PHP computes it with password_hash() and checks a login attempt with password_verify() - the function compares, never you.
The second argument is the algorithm. PASSWORD_DEFAULT tracks the current best choice, so hashes stay strong without code changes. Store the full result in one text column - it already carries the algorithm, the cost, and the salt.
$hash = password_hash($password, PASSWORD_DEFAULT); if (password_verify($password, $hash)) { echo "Welcome back"; }
A login sketch
A login script does four things in order: start the session, load the user row by name, verify the password, and only then mark the session as logged in. Anything else - greeting the user, loading settings - waits until after the check.
On success, rotate the session id with session_regenerate_id(true) so an id seen before login cannot be reused after it. Then store the user id and redirect. The full login flow is covered in Authentication and Authorization in PHP 8.
session_start(); if (password_verify($password, $row["hash"])) { session_regenerate_id(true); $_SESSION["user_id"] = (int) $row["id"]; header("Location: /dashboard.php"); exit; }
Guard pages
Every protected page repeats the same two lines: start the session, then demand a logged-in marker. Put the check at the very top, before any output - headers and redirects stop working once the page starts printing.
session_start(); if (!isset($_SESSION["user_id"])) { header("Location: /login.php"); exit; }
Logout
Logout reverses login: empty the session array, destroy the session on the server, and send the user back to the login page. Destroying without emptying, or emptying without destroying, leaves half a session behind - which is how ghost logins happen.
session_start(); $_SESSION = []; session_destroy(); header("Location: /login.php"); exit;
Next: JSON REST endpoint
Article author: Arthur Isaev