HTTP is stateless, so a server has no built-in way to know that the request it just received came from the same user who logged in a moment ago. Cookies are the oldest standard fix: the server tells the browser to store a small key-value blob, and the browser then automatically attaches that value to every subsequent request to the same site. That lets the server stitch requests into one 'session' and remember login state or a shopping cart.
Set-Cookie and Cookie
A cookie's round trip uses two headers. The server sends Set-Cookie in a response to instruct the browser to store a cookie, and the browser sends the stored value back in the Cookie header on later requests. A response may carry multiple Set-Cookie lines (one per cookie), but the request Cookie header packs all cookies onto a single line separated by semicolons.
HTTP/1.1 200 OK
Set-Cookie: session_id=a1b2c3d4; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600
Set-Cookie: theme=dark; Path=/; Max-Age=31536000
(a later request to the same site)
GET /dashboard HTTP/1.1
Host: app.example.com
Cookie: session_id=a1b2c3d4; theme=dark
Cookie attributes
A cookie's security and behavior are set by the attributes that follow its name and value. Understanding these precisely is the whole game.
HttpOnly: Hides the cookie from JavaScript'sdocument.cookie. It defends against an XSS attacker stealing the session cookie, so it's effectively mandatory for auth cookies.Secure: Only sent over HTTPS. The cookie never leaves over plaintext HTTP, blocking sniffing.SameSite: Controls whether the cookie rides along on cross-site requests.Strict(same-site only),Lax(the default; allowed on top-level navigation GETs),None(cross-site allowed, but requiresSecure).Domain: Which domain the cookie is valid for. Omitted, it binds to exactly the issuing host;Domain=example.comextends it to subdomains.Path: A path prefix that scopes where the cookie is sent.Path=/adminsends it only on requests under/admin.Max-Age/Expires: Lifetime.Max-Ageis seconds (relative);Expiresis an absolute date. With neither, it's a session cookie that dies when the browser closes.
Session auth vs token auth
There are two broad ways to keep state with cookies. Session auth stores session data server-side (memory, Redis, a database) and hands the client only an opaque session ID in a cookie. Because the server holds the state, instant logout/invalidation is easy — but scaling across servers means sharing a session store. Token auth puts the user's claims and a signature inside a token like a JWT that the client carries; the server stores no session, which scales well, but revoking a token before it expires is hard. Putting the token in an HttpOnly cookie can combine some benefits of both.
CSRF: the risk born of automatic sending
The very convenience that the browser attaches cookies automatically becomes a vulnerability. If a user is logged into their bank and then visits an attacker's page, a request that page fires at the bank still carries the session cookie — the browser adds it unconditionally. That is CSRF (Cross-Site Request Forgery). The most effective first line of defense is SameSite=Lax or Strict on auth cookies, so the cookie isn't attached to cross-site requests. Layering a per-request, unpredictable CSRF token (the double-submit pattern) on top of that hardens state-changing endpoints further.
Size limits and the end of third-party cookies
Cookies are for small identifiers, not storage. A single cookie is capped at roughly 4KB, and browsers limit the number per domain (about 50-180). Since they ride on every request, large cookies weigh down all traffic. Keep bulky client state in localStorage or IndexedDB instead.
Meanwhile, third-party cookies — those set by a domain other than the site you're visiting, long used for ads and tracking — are being phased out under privacy pressure. Safari and Firefox already block them by default, and a cookie that doesn't explicitly declare SameSite=None won't be sent in a cross-site context. First-party login sessions are largely unaffected, but if you design cross-domain embeds or SSO, you must account for this shift.