← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

HTTP Cookies in PHP

PHP PHP 7.3+ Beginner
debt(d5/e3/b3/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5), because the detection_hints list semgrep and phpstan as the tools that can catch missing HttpOnly/Secure flags via the pattern `setcookie( without 'httponly'=>true or 'secure'=>true`. These are not default linters but specialist SAST tools, placing this squarely at d5.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3), because the quick_fix is a single replacement of positional args with the options array `['secure'=>true,'httponly'=>true,'samesite'=>'Lax']`. Each setcookie() call is a localised one-call swap, but there may be multiple call sites to update across the codebase, making it slightly more than a one-liner but well within e3 territory.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3), because cookie security flags are set at the call site of setcookie() or in session configuration. The applies_to scope is any PHP web context but the actual fix and ongoing maintenance burden is confined to the specific cookie-setting code paths. The rest of the codebase is largely unaffected.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7), because there are multiple distinct gotchas here that contradict reasonable developer assumptions: the same-request $_COOKIE read failure (the canonical misconception), SameSite=None silently rejected by browsers without Secure=true, and headers-already-sent causing the cookie to silently not be sent. These contradictions are well-documented but counterintuitive, especially the round-trip requirement which contradicts how setting a variable normally works.

About DEBT scoring →

Also Known As

setcookie cookie security Set-Cookie header HttpOnly SameSite

TL;DR

Cookies are small key-value pairs sent by the server via Set-Cookie and echoed back by the browser on every request — PHP sets them with setcookie() before any output, with Secure, HttpOnly, and SameSite flags controlling safety.

Explanation

Cookies are the primary mechanism for persisting state between HTTP requests. PHP sets them with setcookie(name, value, options) — the options array accepts expires (Unix timestamp), path, domain, secure (HTTPS-only), httponly (JS cannot read), and samesite (Strict | Lax | None). The Set-Cookie header is sent as part of the HTTP response; the browser echoes it on subsequent requests via the Cookie header, accessible in PHP as $_COOKIE. Because setcookie() must emit a header, it must be called before any output — even a single space or BOM before <?php will cause a 'headers already sent' error. Cookie values are URL-encoded by default; use setrawcookie() to skip encoding. Secure should always be true in production. HttpOnly blocks JavaScript access, mitigating XSS cookie theft. SameSite=Lax is the browser default and blocks cross-site POST submissions; SameSite=Strict blocks all cross-site sends; SameSite=None requires Secure=true and is needed for embeds or third-party contexts. To delete a cookie, call setcookie() with an expiry in the past.

Watch Out

Never store sensitive data (passwords, PII) in cookie values — cookies are visible to the user and can be tampered with unless signed. Store only a session ID and keep the sensitive data server-side.

Common Misconception

Setting a cookie does not make it available in $_COOKIE on the same request — the browser must complete a round trip first; the new value only appears in $_COOKIE on the next request.

Why It Matters

Missing HttpOnly or Secure flags are consistently exploited in XSS and network-interception attacks — a single misconfigured session cookie can result in full account takeover.

Common Mistakes

  • Calling setcookie() after any output — even whitespace before the opening <?php tag causes 'headers already sent' and the cookie is never sent.
  • Omitting HttpOnly — JavaScript can read the cookie, making XSS attacks trivially able to steal session tokens.
  • Using SameSite=None without Secure=true — modern browsers silently reject the cookie entirely.
  • Reading $_COOKIE immediately after setcookie() on the same request — the value is not present until the next request.

Code Examples

💡 Note
The bad example omits all three security flags — an attacker on the same network can steal the token (no Secure), XSS can read it (no HttpOnly), and CSRF is unmitigated (no SameSite). The options-array form (PHP 7.3+) is the modern way to set all flags clearly.
✗ Vulnerable
// Missing security flags — vulnerable to XSS theft and network interception:
setcookie('session', $token, time() + 3600, '/');
// No Secure, no HttpOnly, no SameSite
✓ Fixed
// Secure cookie with all flags:
setcookie('session', $token, [
    'expires'  => time() + 3600,
    'path'     => '/',
    'secure'   => true,   // HTTPS only
    'httponly' => true,   // JS cannot read
    'samesite' => 'Lax',  // CSRF protection
]);

Added 10 Apr 2026
Views 83
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 1 ping M 1 ping T 1 ping W 0 pings T 1 ping F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F
No pings yet today
PetalBot 1
SEMrush 6 PetalBot 6 Google 5 Ahrefs 5 Perplexity 4 Scrapy 3 Qwen 2 Meta AI 2 Twitter/X 2 Applebot 2 ChatGPT 1 Unknown AI 1 Bing 1
crawler 38 crawler_json 2
🧱 FUNDAMENTALS — new to this? Start with the ground floor.
PHP php A server-side scripting language that generates web pages and APIs — the code runs on the server, and only its output (usually HTML or JSON) reaches the browser.

PHP is often the first server-side language people meet, and understanding its execution model — script starts fresh on every request, no memory between requests — explains most of how the web backend works: sessions, databases, and caching all exist to bridge that per-request amnesia.

💡 Start with PHP 8.x, declare(strict_types=1), and PDO — skip any tutorial that mentions mysql_query().

Ask Codex about PHP →
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Low
⚡ Quick Fix
Replace setcookie() positional args with the options array: ['secure'=>true,'httponly'=>true,'samesite'=>'Lax']
📦 Applies To
PHP 7.3+
🔗 Prerequisites
🔍 Detection Hints
setcookie( without 'httponly'=>true or 'secure'=>true in options array
Auto-detectable: ✓ Yes semgrep phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: High False Positives: Low ✓ Auto-fixable Fix: Low Context: Line
CWE-614 CWE-1004 CWE-1275


✓ schema.org compliant