HTTP cookies are everywhere. Every login session, shopping cart, and analytics tool relies on them. Yet most developers copy-paste a Set-Cookie header and move on without understanding what the attributes actually do — or what happens when they get them wrong.
This guide covers cookie anatomy, every meaningful attribute, how to parse cookie strings, and why SameSite is your CSRF defense. If you want to go hands-on right away, try the IO Tools Cookie Parser ou le Cookie Builder.
What a Cookie Actually Looks Like
When a server wants to set a cookie, it sends a Set-Cookie response header:
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=86400
The browser stores this and sends it back on subsequent requests as:
Cookie: sessionId=abc123
That is the whole mechanism. The complexity is in those attributes.
Cookie Anatomy: Breaking Down the String
A cookie string follows a consistent format:
name=value; attribute1; attribute2=attributeValue; ...
Le name=value pair comes first. Everything after the first semicolon is a directive to the browser — the server never sees these attributes in the Cookie header it receives.
The Security Attributes You Must Get Right
HttpOnly
HttpOnly prevents JavaScript from reading the cookie via document.cookie. This is a direct defense against XSS attacks stealing session tokens.
Set-Cookie: sessionId=abc123; HttpOnly
Any cookie that authenticates a user should have HttpOnly. There is no good reason not to.
Sécurisé
Secure means the browser only sends the cookie over HTTPS connections. Without it, the cookie travels in plaintext over HTTP and can be intercepted. In production, session cookies always need Secure. In local development over http://localhost, you can omit it — browsers make an exception for localhost.
SameSite
SameSite controls when the browser includes a cookie in cross-site requests. This is the primary defense against CSRF attacks. Three values:
Strict— cookie is never sent on cross-site requests. Most secure, but users clicking a link from an email arrive logged out (the cookie is not sent on that initial navigation).Lax— cookie is sent on top-level navigations (GET requests) from external sites, but not on embedded cross-site requests or cross-site POSTs. This is the browser default for cookies without an explicitSameSiteattribute.None— cookie is sent on all cross-site requests. Required for third-party cookies (OAuth flows, embedded widgets). Must be paired withSecure.
# Third-party / cross-site cookie (e.g., OAuth callback)
Set-Cookie: token=xyz; SameSite=None; Secure
If you send SameSite=None sans Secure, modern browsers reject the cookie entirely. For most session cookies, use SameSite=Lax — it balances security with a usable login experience.
Scope: Domain and Path
Domaine
Le Domain attribute specifies which hostnames receive the cookie.
Set-Cookie: user=alice; Domain=example.com
With Domain=example.com, the cookie is sent to example.com and all subdomains (api.example.com, app.example.com). Without a Domain attribute, the cookie is sent only to the exact origin that set it — not to subdomains.
Common misconception: setting Domain=example.com does pas restrict the cookie to just example.com. It broadens scope to include subdomains. Omit the attribute if you want a single-host cookie.
Chemin
Path limits which URL paths trigger the cookie being sent.
Set-Cookie: adminToken=xyz; Path=/admin
This cookie only accompanies requests to /admin and paths below it. A request to / ou /api would not include it. The default is /, meaning all paths.
Max-Age vs Expires
Both control when a cookie expires. Prefer Max-Age.
Expirestakes an absolute date in HTTP date format. This is relative to the client’s clock, which you do not control.Max-Agetakes a number of seconds from now:Max-Age=86400for 24 hours. Relative to the server’s intent, not the client’s clock.
When both are present, Max-Age takes precedence. A cookie with neither attribute is a session cookie — it disappears when the browser closes.
Cookie Attribute Reference
| Attribute | What It Does | Défaut | Recommandation |
|---|---|---|---|
HttpOnly | Blocks JavaScript access to the cookie | Not set | Always set for auth cookies |
Secure | HTTPS-only transmission | Not set | Always set in production |
SameSite | Controls cross-site sending | Lax (modern browsers) | Lax for sessions; None + Secure for third-party |
Domain | Sets hostname scope | Current host only | Omit unless cross-subdomain access is needed |
Path | Sets path scope | / | Leave as / unless isolating admin tokens |
Max-Age | Seconds until expiry | Session cookie | Prefer over Expires |
Expires | Absolute expiry date | Session cookie | Use Max-Age instead |
Setting Cookies in Code
Node.js (Express)
app.post('/login', (req, res) => {
// ... verify credentials ...
res.cookie('sessionId', token, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
maxAge: 86400 * 1000, // milliseconds in Express
path: '/',
});
res.json({ ok: true });
});
Python (FastAPI)
from fastapi import FastAPI, Response
app = FastAPI()
@app.post("/login")
def login(response: Response):
# ... verify credentials ...
response.set_cookie(
key="sessionId",
value=token,
httponly=True,
secure=True,
samesite="lax",
max_age=86400,
path="/",
)
return {"ok": True}
Parsing Cookie Headers Manually
Le Cookie header the server receives contains only name=value pairs, separated by ; :
Cookie: sessionId=abc123; theme=dark; lang=en
To parse it manually: split on ; (semicolon + space), then split each pair on the first = only. Edge cases worth knowing:
- Values can contain
=(base64 strings commonly do) — always split on the first=only - Cookie names are case-sensitive
- Whitespace around the separator can vary — trim both sides defensively
Rather than write the split logic yourself, use the IO Tools Cookie Parser to decode any Cookie header and inspect each value, or the IO Tools Cookie Builder to construct a valid Set-Cookie header with the right attributes.
Cookies and CSRF
Cross-Site Request Forgery exploits the fact that browsers automatically include cookies on requests to a domain, even when initiated by a different site. A malicious page at evil.com can submit a form to bank.com/transfer, and if the user is logged in, the browser sends their session cookie along with the forged request.
SameSite=Lax defeats most CSRF vectors because cross-site POST requests — the typical attack pattern — do not include the cookie. SameSite=Strict is even more thorough but can affect usability.
CSRF tokens remain valid as defense-in-depth, especially for high-stakes operations and when SameSite=None is required for third-party context. The two defenses complement each other.
Installez nos extensions
Ajoutez des outils IO à votre navigateur préféré pour un accès instantané et une recherche plus rapide
恵 Le Tableau de Bord Est Arrivé !
Tableau de Bord est une façon amusante de suivre vos jeux, toutes les données sont stockées dans votre navigateur. D'autres fonctionnalités arrivent bientôt !
Outils essentiels
Tout voir Nouveautés
Tout voirMise à jour: Notre dernier outil was added on Avr 28, 2026
