You need three tools to start: a web server (Apache), a database (MySQL), and PHP. The easiest way is to install XAMPP — it gives you all three in one package. No separate setup needed.
MySQL Database
MySQL ডেটাবেস
Create a database and a users table
Sign Up Page
সাইন আপ পেজ
Register new users safely
Log In Page
লগইন পেজ
Verify user credentials with sessions
Log Out Page
লগআউট পেজ
Destroy session and protect pages
Security Rules
নিরাপত্তা নিয়ম
Hashing, prepared statements, XSS
Dashboard Page
ড্যাশবোর্ড পেজ
A page only logged-in users can see
Download XAMPP
XAMPP ডাউনলোড করুন
Go to apachefriends.org and download XAMPP for your system (Windows, Mac, or Linux). Run the installer and accept defaults.
Start Apache and MySQL
Apache এবং MySQL চালু করুন
Open the XAMPP Control Panel. Click Start next to Apache and MySQL. Both must show a green light before you continue.
Create your project folder
প্রজেক্ট ফোল্ডার তৈরি করুন
Go to C:/xampp/htdocs/ and create a new folder called myauth. All your PHP files go inside this folder.
Open your project in the browser
ব্রাউজারে প্রজেক্ট খুলুন
Type localhost/myauth/ in your browser. This is where you will see your work.
myauth/ → db.php, signup.php, login.php, logout.php, dashboard.php
| File | Purpose |
|---|---|
db.php | Connects to the MySQL database. All other files use this. |
signup.php | Shows the registration form and saves new users. |
login.php | Shows the login form and starts a session on success. |
logout.php | Destroys the session and sends the user to login.php. |
dashboard.php | A protected page. Only logged-in users can see it. |
A database stores your user data. You create one table called users. Each row in this table is one user account — with an id, name, email, password, and the date they joined.
Step 1 — Open localhost/phpmyadmin in your browser. Click the SQL tab and run this:
ধাপ ১ — ব্রাউজারে localhost/phpmyadmin খুলুন। SQL ট্যাবে ক্লিক করুন এবং এটি চালান:
CREATE DATABASE myauth_db; USE myauth_db; CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL, email VARCHAR(150) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
| Column | Type | Why? |
|---|---|---|
id | INT AUTO_INCREMENT | Unique number for each user. Grows automatically. |
name | VARCHAR(100) | The user's full name. Up to 100 characters. |
email | VARCHAR(150) UNIQUE | The email must be unique — no two users share one. |
password | VARCHAR(255) | Stores the hashed password. Needs 255 chars for safety. |
created_at | TIMESTAMP | Records the exact date and time the user signed up. |
Step 2 — Create db.php. Every other file will include this file to connect to MySQL:
ধাপ ২ — db.php তৈরি করুন। MySQL এ সংযুক্ত হতে অন্য সব ফাইল এই ফাইলটি অন্তর্ভুক্ত করবে:
<?php // Database connection settings $host = "localhost"; $dbname = "myauth_db"; $username = "root"; // XAMPP default username $password = ""; // XAMPP default password (empty) try { $pdo = new PDO( "mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password ); // Show errors clearly while you develop $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // Return rows as associative arrays $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); } catch (PDOException $e) { die("Connection failed: " . $e->getMessage()); } ?>
PDO (PHP Data Objects) is the modern way to talk to MySQL in PHP. It blocks SQL injection attacks. Always use PDO. Never use the old mysql_connect() function.
The sign up page collects a name, email, and password. It checks the email is not already taken. Then it hashes the password and saves the new user to the database. Never store a plain password!
<?php require_once 'db.php'; // Load the database connection $error = ''; $success = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Step 1: Get and clean the form inputs $name = trim($_POST['name']); $email = trim($_POST['email']); $password = $_POST['password']; // Step 2: Check all fields are filled if (empty($name) || empty($email) || empty($password)) { $error = "All fields are required."; // Step 3: Check the email format is correct } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) { $error = "Enter a valid email address."; // Step 4: Check the password is long enough } elseif (strlen($password) < 6) { $error = "Password must be at least 6 characters."; } else { // Step 5: Check if this email is already registered $stmt = $pdo->prepare("SELECT id FROM users WHERE email = ?"); $stmt->execute([$email]); if ($stmt->rowCount() > 0) { $error = "This email is already registered."; } else { // Step 6: Hash the password — NEVER store plain text $hashed = password_hash($password, PASSWORD_DEFAULT); // Step 7: Insert the new user into the database $stmt = $pdo->prepare( "INSERT INTO users (name, email, password) VALUES (?, ?, ?)" ); $stmt->execute([$name, $email, $hashed]); $success = "Account created! You can now log in."; } } } ?> <!DOCTYPE html> <html><body> <h2>Create Account</h2> <?php if ($error): ?><p style="color:red"><?= $error ?></p><?php endif; ?> <?php if ($success): ?><p style="color:green"><?= $success ?></p><?php endif; ?> <form method="POST"> <input type="text" name="name" placeholder="Full Name" required> <input type="email" name="email" placeholder="Email" required> <input type="password" name="password" placeholder="Password" required> <button type="submit">Sign Up</button> </form> <p>Already have an account? <a href="login.php">Log In</a></p> </body></html>
password_hash($password, PASSWORD_DEFAULT) turns "hello123" into a scrambled string like $2y$10$abc.... Even if someone steals your database, they cannot read the original passwords. Always use this function.
trim() removes extra spaces from the start and end of a value. For example, " hello " becomes "hello". This stops users from signing up with emails like " user@gmail.com " (with accidental spaces).
The login page checks the email and password the user types. It finds the user in the database, then uses password_verify() to check the password. If it matches, it starts a session — PHP's way of remembering "this user is logged in."
<?php session_start(); // Always call this FIRST — before any output require_once 'db.php'; // If the user is already logged in, send them to dashboard if (isset($_SESSION['user_id'])) { header("Location: dashboard.php"); exit; } $error = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $email = trim($_POST['email']); $password = $_POST['password']; // Step 1: Check fields are not empty if (empty($email) || empty($password)) { $error = "Please enter your email and password."; } else { // Step 2: Find user by email (prepared statement) $stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?"); $stmt->execute([$email]); $user = $stmt->fetch(); // Step 3: Check user exists AND password matches if ($user && password_verify($password, $user['password'])) { // Step 4: Save user info in session (login success!) $_SESSION['user_id'] = $user['id']; $_SESSION['user_name'] = $user['name']; $_SESSION['user_email']= $user['email']; // Step 5: Send to dashboard header("Location: dashboard.php"); exit; } else { // Generic error — never say "wrong password" specifically $error = "Invalid email or password."; } } } ?> <!DOCTYPE html> <html><body> <h2>Log In</h2> <?php if ($error): ?> <p style="color:red"><?= $error ?></p> <?php endif; ?> <form method="POST"> <input type="email" name="email" placeholder="Email" required> <input type="password" name="password" placeholder="Password" required> <button type="submit">Log In</button> </form> <p>No account yet? <a href="signup.php">Sign Up</a></p> </body></html>
A session keeps data on the server for one user. When you set $_SESSION['user_id'], PHP saves a small cookie in the user's browser. On every next page, PHP reads that cookie and knows who the user is. Think of it as a wristband at an event — you get it at the door and show it on every ride.
We say "Invalid email or password." — not "Wrong password." This is on purpose. If you say "Wrong password," a hacker knows the email is correct and keeps guessing passwords. A generic message gives nothing away.
Logging out means you destroy the session. This removes all saved user data from the server. You also need to protect private pages — if a user is not logged in, send them back to the login page right away.
<?php session_start(); // Step 1: Clear all session variables $_SESSION = []; // Step 2: Delete the session cookie from the browser if (ini_get("session.use_cookies")) { $params = session_get_cookie_params(); setcookie( session_name(), '', time() - 42000, // Set expiry to the past to delete it $params["path"], $params["domain"] ); } // Step 3: Destroy the session data on the server session_destroy(); // Step 4: Send the user back to the login page header("Location: login.php"); exit; ?>
Add this guard block at the very top of any page you want to protect. If the user is not logged in, PHP sends them to login.php right away.
আপনি যে পেজটি রক্ষা করতে চান তার একদম উপরে এই গার্ড ব্লক যোগ করুন। ব্যবহারকারী লগইন না থাকলে PHP তাকে সাথে সাথে login.php তে পাঠায়।
<?php session_start(); // Guard: if not logged in → send to login page immediately if (!isset($_SESSION['user_id'])) { header("Location: login.php"); exit; } ?> <!DOCTYPE html> <html><body> <h2> Welcome, <?= htmlspecialchars($_SESSION['user_name']) ?>! </h2> <p>You are logged in as <?= htmlspecialchars($_SESSION['user_email']) ?></p> <a href="logout.php">Log Out</a> </body></html>
When you show user data on a page, always wrap it in htmlspecialchars(). This stops XSS attacks where a user saves HTML or JavaScript in their name (for example, <script>steal cookies</script>).
password_hash() to save passwords. Use password_verify() to check them.$pdo->prepare() with ? placeholders. Never put a variable directly in an SQL string.htmlspecialchars() when you show user data in HTML. This blocks XSS attacks.empty(), filter_var(), and strlen() to check every value before using it.| # | Rule | PHP Function | Blocks |
|---|---|---|---|
| 1 | Hash passwords | password_hash() | Data theft |
| 2 | Prepared statements | $pdo->prepare() | SQL Injection |
| 3 | Escape output | htmlspecialchars() | XSS |
| 4 | Validate inputs | filter_var(), empty() | Bad data |
| 5 | HTTPS | SSL certificate | Man-in-the-middle |
Never build SQL like this: "SELECT * FROM users WHERE email = '$email'". A hacker can type ' OR 1=1 -- as the email and log in without a password. Always use prepare() with ? placeholders instead.
// WRONG — SQL injection possible $email = $_POST['email']; $sql = "SELECT * FROM users WHERE email = '$email'"; $result = $pdo->query($sql);
// CORRECT — prepared statement with placeholder $email = $_POST['email']; $stmt = $pdo->prepare("SELECT * FROM users WHERE email = ?"); $stmt->execute([$email]); $user = $stmt->fetch();
password_verify() safely checks the typed password against the hashed one in the database.password_verify($password, $hash). Functions like md5 and sha1 are insecure for passwords.session_start() must be the very first line before any HTML output.session_start(). It must come before any HTML output, even a blank line.$pdo->prepare("SELECT * FROM users WHERE id = ?") protect against?session_destroy() do?session_destroy() removes all session data from the server. This is the core of the log out process.session_destroy() destroys all session data stored on the server — this is what logs the user out.htmlspecialchars() converts characters like < and > to safe HTML entities, stopping scripts from running.htmlspecialchars($data). It converts HTML special characters to safe entities so scripts cannot run.