PHP Cookies
A cookie is a small piece of data that a PHP script asks the visitor’s browser to store and send back with every future request to the same site. Cookies are how a stateless protocol like HTTP can “remember” things between page loads — a logged-in user, a language preference, an item in a shopping cart. In PHP, you create cookies with the setcookie() function and read them back through the $_COOKIE superglobal.
Overview / How Cookies Work
HTTP itself has no memory. Every request a browser makes is independent of the last, so without some mechanism to carry state, a server could never tell that two requests came from the same visitor. Cookies solve this at the protocol level: when PHP calls setcookie(), it does not modify a database or a session file directly — it adds a Set-Cookie header to the HTTP response. The browser reads that header, stores the name/value pair (along with its expiry, domain, and path rules), and on every subsequent request to a matching URL, it automatically attaches a Cookie header listing all the cookies it has stored for that site.
PHP parses the incoming Cookie header for you and populates the $_COOKIE superglobal array before your script runs, so reading a cookie is as simple as reading any other array. Setting one, however, is more delicate: because setcookie() works by sending an HTTP header, it must run before any other output is sent to the browser — before any HTML, whitespace, or even a stray newline outside the <?php tags. Once the HTTP headers are flushed, PHP cannot add another one, and setcookie() will fail with a “headers already sent” warning.
Also crucial: a cookie you set with setcookie() in the current request is not immediately available in $_COOKIE during that same request (unless you also manually add it to the array). It becomes visible starting with the browser’s next request, because it has to make the full round trip: server sends Set-Cookie → browser stores it → browser sends it back as Cookie on the next request → PHP populates $_COOKIE.
Syntax
bool setcookie(
string $name,
string $value = "",
array $options = []
)
Modern PHP (7.3+) also supports an options array instead of separate positional arguments for expiry, path, domain, secure, and httponly. The table below covers the meaning of each part.
| Part | Description |
|---|---|
name |
The cookie’s name. Retrieved later as $_COOKIE['name']. |
value |
The string stored in the cookie. Automatically URL-encoded by setcookie() (use setrawcookie() to skip encoding). |
expires |
A Unix timestamp (e.g. time() + 3600) for when the cookie should expire. Omit or set to 0 for a “session cookie” that disappears when the browser closes. |
path |
The URL path the cookie applies to. '/' makes it available across the whole domain. |
domain |
The domain the cookie is valid for, e.g. '.example.com' to share it across subdomains. |
secure |
If true, the cookie is only sent over HTTPS connections. |
httponly |
If true, JavaScript (document.cookie) cannot read the cookie — a strong defense against XSS-based theft. |
samesite |
'Lax', 'Strict', or 'None'. Controls whether the cookie is sent on cross-site requests, mitigating CSRF. |
Examples
Example 1: Setting and reading a simple cookie
<?php
// Must run before any HTML/output is sent
setcookie('username', 'alice', time() + 3600, '/');
if (isset($_COOKIE['username'])) {
echo "Welcome back, " . htmlspecialchars($_COOKIE['username']) . "!";
} else {
echo "No username cookie set yet.";
}
Output:
No username cookie set yet.
This is the output on the very first request, because the cookie was only just sent to the browser in this response — it will not appear in $_COOKIE until the visitor’s next request, when the browser sends it back.
Example 2: Using the options array (PHP 7.3+)
<?php
declare(strict_types=1);
function rememberLanguage(string $lang): bool
{
return setcookie('lang', $lang, [
'expires' => time() + (86400 * 30), // 30 days
'path' => '/',
'domain' => '',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
}
$ok = rememberLanguage('en-US');
echo $ok ? 'Cookie queued for sending.' : 'Failed to set cookie.';
Output:
Cookie queued for sending.
The options array is the recommended modern syntax because it is self-documenting and lets you set samesite, which has no equivalent positional argument in the older function signature. setcookie() returns true if it successfully queued the header (it does not confirm the browser actually stored it).
Example 3: Reading all cookies and deleting one
<?php
// List every cookie sent by the browser
foreach ($_COOKIE as $name => $value) {
echo "$name = " . htmlspecialchars($value) . "\n";
}
// To delete a cookie, set it with a past expiry time and empty value
setcookie('lang', '', [
'expires' => time() - 3600,
'path' => '/',
]);
echo "Requested deletion of 'lang' cookie.";
Output:
username = alice
lang = en-US
Requested deletion of 'lang' cookie.
This example assumes the username and lang cookies were already stored from earlier requests. Note that there is no deletecookie() function in PHP — deleting is really just overwriting the cookie with an expiry time in the past, which tells the browser to discard it immediately. Crucially, the path and domain used to delete a cookie must match the ones used to create it, or the browser will treat it as an unrelated cookie and leave the original untouched.
How It Works Step by Step
- Your script calls
setcookie('name', 'value', ...)before any output has been sent. - PHP appends a
Set-Cookie: name=value; expires=...; path=/; HttpOnlyheader to the outgoing HTTP response. - The browser receives the response, parses the
Set-Cookieheader, and stores the cookie in its cookie jar for the matching domain/path. - On every future request to a matching URL, the browser automatically attaches a single
Cookie: name=value; other=value2header. - PHP parses that incoming header at the start of the request and fills
$_COOKIEbefore your script code runs. - When the cookie’s expiry time passes (or you overwrite it with a past expiry), the browser removes it from its jar and stops sending it.
Common Mistakes
Mistake 1: Calling setcookie() after output has started.
<?php
echo "<h1>Welcome</h1>";
setcookie('username', 'alice'); // Fails: headers already sent
Because the echo already flushed body content, PHP can no longer inject an HTTP header. Move every setcookie() call above any HTML, whitespace, or echo — ideally at the very top of the script, or use output buffering (ob_start()) if that is not possible.
Mistake 2: Confusing the expiry argument with a duration.
<?php
// Wrong: this expires the cookie at 3600 seconds since 1970 (long past)
setcookie('token', 'abc123', 3600);
expires is an absolute Unix timestamp, not a number of seconds from now. Always add it to time():
<?php
setcookie('token', 'abc123', time() + 3600); // expires in 1 hour
Best Practices
- Always set
httponlytotruefor cookies that don’t need JavaScript access, to block theft via XSS. - Always set
securetotruein production so cookies are never sent over plain HTTP. - Set
samesiteto'Lax'or'Strict'to reduce CSRF risk unless you have a specific cross-site need for'None'. - Never store sensitive data (passwords, raw credit card numbers, unsigned user IDs) directly in a cookie — cookies are stored on the client and can be edited or copied by the user. Use
$_SESSIONfor sensitive state, storing only an opaque session ID in the cookie. - Always run
setcookie()before any output; check for accidental whitespace before<?phpin included files. - Use
htmlspecialchars()when echoing cookie values back into HTML to avoid XSS. - Use a matching
path/domainwhen deleting a cookie, or it won’t actually be removed.
Practice Exercises
- Write a script that sets a
themecookie (value'dark'or'light') that expires in 7 days, then on a later request reads it and echoes a message like “Using dark theme.” - Write a function
clearAllCookies(): voidthat loops over$_COOKIEand expires every cookie the browser sent, using the correct past-timestamp technique. - Modify Example 2 so the cookie only applies to the
/accountpath instead of the whole site, and explain in a comment why a request to/blogwould then no longer receive that cookie.
Summary
- Cookies let a stateless HTTP server “remember” a visitor across requests by asking the browser to store and resend small pieces of data.
setcookie()sends aSet-CookieHTTP header and must be called before any other output.- A newly set cookie appears in
$_COOKIEstarting with the browser’s next request, not the current one. - Use the options array form of
setcookie()to controlexpires,path,domain,secure,httponly, andsamesite. - Deleting a cookie means re-setting it with a past expiry time and matching path/domain.
- Never store sensitive data in a cookie directly; prefer sessions and always set
httponlyandsecurefor anything security-related.
