What a cookie is
HTTP is stateless: each request arrives on its own, and the server does not automatically know that two requests came from the same person. Cookies fix that. A cookie is a small named value that your server asks the browser to store. The browser then sends it back with every later request to your site, so you can recognise the visitor or remember their choices.
Typical uses: a preferred language or theme, a "recently viewed" list, a consent choice, and most importantly the session ID that links a visitor to their session.
Setting a cookie
setcookie() adds a Set-Cookie header to the response. Like any header, it must be sent before any output. The modern form takes an options array:
<?php
$theme = $_GET['theme'] ?? null;
if (in_array($theme, ['light', 'dark'], true)) {
setcookie('theme', $theme, [
'expires' => time() + 60 * 60 * 24 * 365, // one year
'path' => '/',
'httponly' => true,
'samesite' => 'Lax',
]);
echo "Theme saved: $theme";
exit;
}
$current = $_COOKIE['theme'] ?? 'light';
if (!in_array($current, ['light', 'dark'], true)) {
$current = 'light';
}
echo "Current theme: $current";The response to /theme.php?theme=dark carries the cookie in a header. The expires date depends on when you run it, so it is not shown here:
GET /theme.php?theme=darkOn the next request, the browser sends the cookie back and PHP puts it in $_COOKIE:
GET /theme.phpCurrent theme: darkThat one-request delay surprises people: inside the request that calls setcookie(), $_COOKIE still contains only what the browser sent with that request. If you need the new value straight away, use the variable you just stored.
Notice also that the cookie value is checked against the allowed list when it is read. It came from the browser, so it is user input like any other.
The options
| Option | Meaning |
|---|---|
expires |
Unix timestamp when it expires. Leave it out (or 0) for a session cookie, deleted when the browser closes. |
path |
URL path the cookie is sent to. / means the whole site. |
domain |
Leave it out to restrict the cookie to the exact host. |
secure |
Only send over HTTPS. Turn it on for every live site. |
httponly |
Hide the cookie from JavaScript (document.cookie). |
samesite |
Lax (sent on normal links to your site, not on cross-site form posts), Strict (never sent from other sites) or None (always, requires secure). |
secure is left out in the example above only because the course's test server uses plain HTTP. On a real site with HTTPS, set 'secure' => true. HttpOnly and SameSite=Lax are good defaults for almost every cookie: the first limits what a successful XSS attack can steal, and the second blocks most cross-site request forgery, which the web security lesson explains.
Deleting a cookie
There is no "delete" command. You overwrite the cookie with an expiry time in the past, using the same path, and the browser throws it away:
<?php
setcookie('theme', '', ['expires' => time() - 3600, 'path' => '/']);
unset($_COOKIE['theme']); // also forget it for the rest of this request
echo "Theme reset";GET /reset-theme.phpTheme resetGET /theme.phpCurrent theme: lightWhat not to store in cookies
The browser stores cookies on the visitor's computer, and the visitor can read and edit them with developer tools in seconds. So:
- Never store passwords or other secrets.
- Never store anything you trust, such as a price, a discount, a user ID that grants access, or
is_admin=1. Anyone could change it. - Keep them small. Browsers limit a cookie to about 4 KB, and every cookie is sent with every request, including requests for images.
For anything that must not be tampered with, store the data on the server in a session and let the cookie hold only a random session ID. That is the next lesson.
Cookies and privacy law
Cookies that are strictly necessary for the site to work, such as a session cookie or a shopping cart, are generally allowed. Cookies used for analytics or advertising usually need the visitor's consent under laws such as the GDPR in Europe and the UK, and India's Digital Personal Data Protection Act also requires a clear purpose and consent for personal data. Explain what you store in your privacy or cookie policy.
Common mistakes
- Calling setcookie() after output, which fails with "headers already sent".
- Expecting $_COOKIE to contain the new value in the same request.
- Trusting cookie values. Validate them like any input.
- Deleting a cookie with a different path from the one it was set with, which leaves the original in place.
Exercise
Write visits.php that counts how many times the visitor has loaded the page, using a cookie called visits that lasts 30 days. Print "Visit number N". Make sure a tampered value such as "abc" or "-5" starts the count again at 1.
Show one possible answer
<?php
$visits = filter_var($_COOKIE['visits'] ?? 0, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]]);
$visits = ($visits === false ? 0 : $visits) + 1;
setcookie('visits', (string) $visits, [
'expires' => time() + 60 * 60 * 24 * 30,
'path' => '/',
'httponly' => true,
'samesite' => 'Lax',
]);
echo "Visit number $visits";GET /visits.phpVisit number 1GET /visits.phpVisit number 2Because a visitor can change the cookie, this count is only good for harmless features such as "welcome back" messages, never for anything that matters.
Key points
- A cookie is a small piece of text the browser stores and sends back with every request to your site.
- setcookie() sends a cookie (before any output); the value appears in $_COOKIE on the next request, not the current one.
- Use the options array: expires, path, secure, httponly and samesite. Delete a cookie by setting an expiry in the past.
- Visitors can read and change cookies, so never store passwords, prices or anything you trust in them.