Sessions Cookies
By the end of this lesson you'll be able to remember a user across page loads — keeping login state in a secure server-side session and storing harmless preferences in a browser cookie, the right way.
Part of the free Php course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
1️⃣ Sessions: Remembering a User
The web is stateless — by default the server forgets everything the moment a page finishes loading, so the next click looks like a brand-new stranger. A session fixes that. Calling session_start() gives the visitor a unique ID, stores that ID in a cookie called PHPSESSID , and opens a private file on the server to hold their data. You read and write that data through the $_SESSION superglobal — an array PHP makes available in every file. Because the data lives on the server, the user can't see or tamper with it.
One rule dominates everything here: session_start() must run before any output — no HTML, no echo , not even a stray blank line before < ?php . It sends an HTTP header, and headers must come first. Put it at the very top of the file, every time.
2️⃣ Reading & Removing Session Data
On a later page you call session_start() again — not to recreate the data, but to reconnect to it using the ID the browser sent back. Always guard a read with isset() , which returns true only when a key exists; reading a missing key throws a warning. To remove data you have two tools: unset($_SESSION['key']) deletes a single value, and you'll see session_destroy() below for clearing everything.
3️⃣ The Login / Logout Pattern
Login state is just session data. When a login succeeds you store something that identifies the user (a user_id ); protected pages then check isset($_SESSION['user_id']) to decide who gets in; logout clears it. The one line beginners skip is session_regenerate_id(true) at login — it swaps in a fresh session ID so an attacker can't hijack the session (more on this in Security below). Logout empties $_SESSION and calls session_destroy() to delete the server file.
4️⃣ Cookies: Storing Data in the Browser
A cookie is a small key-value pair (up to about 4KB) that PHP asks the browser to store and send back on every future request. Use cookies for things that aren't secret — a theme, a chosen language, a "remember me" flag. You create one with setcookie() , and the modern form takes an options array so the security flags are clearly named. Like session_start() , it sends a header, so it must run before any output.
Each option earns its place: expires is a UNIX timestamp for when the cookie dies (omit it and the cookie vanishes when the browser closes); path set to '/' makes the cookie apply site-wide; secure restricts it to HTTPS; httponly hides it from JavaScript; and samesite blocks it on cross-site requests. The last three are your front-line defence and you'll meet them again in Security .
5️⃣ Reading & Deleting Cookies
Here's the catch that trips everyone the first time: a cookie you just set is not in $_COOKIE on the same request. setcookie() only tells the browser to store it; the browser sends it back starting from the next request, which is when PHP fills the $_COOKIE superglobal. To read it, guard with isset() or the null-coalescing ?? operator for a fallback. To delete a cookie, re-set it with an expiry in the past .
6️⃣ Security: Fixation, httponly & secure
State is where most beginner security holes live, so internalise three habits. Session fixation: an attacker gets a victim to use a session ID the attacker already knows, then waits for the victim to log in and rides the now-authenticated session. Defeat it by calling session_regenerate_id(true) the instant a login succeeds — the ID changes, so the attacker's copy is worthless. Cookie theft: a cross-site scripting (XSS) flaw lets injected JavaScript read document.cookie — unless the cookie is httponly: true , which hides it from JavaScript entirely. Eavesdropping & CSRF: secure: true keeps the cookie off plain HTTP, and samesite: 'Strict' (or 'Lax' ) stops it riding along on requests from other sites. For any cookie tied to a login, set all three — and never store passwords or raw tokens in a cookie; keep secrets in the session and store only a hashed token if you must persist one.
7️⃣ Your Turn: Check Login State
Now you try. The script below is almost complete — fill in each ___ using the 👉 hint, then run it and check it against the Output panel.
One more. This time you set a secure cookie and read it back with a sensible default. Fill in the function name and the two security flags.
Common Errors (and the fix)
- "Warning: session_start(): Cannot start session — headers already sent" — something printed before session_start() (or setcookie() ). The usual culprit is a blank line or space before the opening < ?php tag, or an echo above it. Move both calls to the very top and strip any whitespace before < ?php .
- Login "works" but the session can be hijacked — you didn't regenerate the ID. After every successful login call session_regenerate_id(true) so the session ID changes; otherwise an attacker who fixed the ID earlier rides your logged-in session (session fixation).
- A login cookie can be read by JavaScript or sent over plain HTTP — you left off the security flags. Set 'httponly' => true (blocks JavaScript / XSS theft) and 'secure' => true (HTTPS only), and add 'samesite' => 'Strict' to blunt CSRF.
- "Undefined array key" when reading $_SESSION or $_COOKIE — you read a key that isn't there. Guard with isset($_SESSION['key']) or use a default: $_COOKIE['x'] ?? 'fallback' .
- $_COOKIE is empty right after setcookie() — that's expected, not a bug. The cookie only arrives on the next request. Reload the page (or move to the next one) and it will be there.
Pro Tips
- 💡 Secrets go in the session, not the cookie. The browser only ever needs the meaningless PHPSESSID — keep usernames, roles, and tokens server-side in $_SESSION .
- 💡 Regenerate on every privilege change , not just login — for example after switching to an admin area. It's cheap insurance against fixation.
- 💡 Prefer the array form of setcookie() (PHP 7.3+). Named options like 'samesite' are clearer and harder to get wrong than a long list of positional arguments.
📋 Quick Reference — Sessions & Cookies
Syntax
Example
What It Does
session_start()
session_start();
Start/resume the session (before any output)
$_SESSION['k']
$_SESSION['id'] = 7;
Read / write server-side session data
unset()
unset($_SESSION['k']);
Remove one session variable
session_destroy()
session_destroy();
Destroy the whole session (logout)
session_regenerate_id()
session_regenerate_id(true);
New ID — prevents session fixation
setcookie()
setcookie('t','dark',[...]);
Create / update / delete a cookie
$_COOKIE['name']
$_COOKIE['t'] ?? 'light'
Read a cookie (next request)
Frequently Asked Questions
Mini-Challenge: A Page-View Counter
No code is filled in this time — just a brief and an outline. Write it yourself, serve it with php -S localhost:8000 , then refresh the page a few times and watch the count climb. This is the same store-read-update loop behind every shopping cart and login on the web.
🎉 Lesson Complete!
- ✅ HTTP is stateless ; a session remembers a user via the PHPSESSID cookie and server-side storage
- ✅ session_start() must run before any output ; read/write data through $_SESSION
- ✅ Guard reads with isset() ; remove with unset() or session_destroy()
- ✅ Cookies live in the browser — set them with setcookie() and read them from $_COOKIE on the next request
- ✅ Lock cookies down with secure , httponly , and samesite ; regenerate the ID at login to stop fixation
- ✅ Next lesson: Object-Oriented PHP — organise your code into reusable classes and objects
Practice quiz
Where is session data stored?
- Entirely in the browser cookie
- In the URL query string
- On the server; only the session ID is in the browser
- In a hidden form field
Answer: On the server; only the session ID is in the browser. Session data lives in a file on the server; the browser only holds the meaningless PHPSESSID cookie.
What must be true about calling session_start() and setcookie()?
- They must run before any output (HTML or echo)
- They must run after the HTML body
- They can be called anywhere
- They only work inside functions
Answer: They must run before any output (HTML or echo). Both send HTTP headers, which must come before any page content — otherwise 'headers already sent'.
Why call session_regenerate_id(true) right after a successful login?
- To log the user out
- To clear all cookies
- To speed up the session
- To prevent session fixation by swapping in a fresh ID
Answer: To prevent session fixation by swapping in a fresh ID. Regenerating the ID at login means an attacker's pre-planted ID no longer points at the logged-in session.
When does a cookie you just set with setcookie() appear in $_COOKIE?
- Immediately on the same request
- On the next request, not the current one
- Never
- Only after session_destroy()
Answer: On the next request, not the current one. setcookie() only sends a header; the browser returns the cookie starting from the next request.
Which cookie flag hides the cookie from JavaScript to block XSS theft?
- httponly
- secure
- samesite
- path
Answer: httponly. httponly: true hides the cookie from document.cookie, so injected JavaScript cannot read it.
Which cookie flag ensures the cookie is only sent over HTTPS?
- httponly
- samesite
- secure
- expires
Answer: secure. secure: true keeps the cookie off plain HTTP, so it is never exposed in transit.
How do you delete a cookie?
- name
Re-setting the cookie with expires in the past (e.g. time() - 3600) tells the browser to discard it.
What is the right pair of steps to log a user out?
- user_id
Continue this course
- Previous: Forms & User Input
- Next: Object-Oriented PHP