Cookies Explained: Session, Third-Party, Secure and HttpOnly
Cookie types (session, persistent, third-party) and what each security attribute — Secure, HttpOnly, SameSite — protects against.
HTTP has no memory. Every request stands alone, so on its own a server cannot tell the user who just logged in from a complete stranger. Cookies are the patch for that gap: the server attaches a Set-Cookie header to a response, the browser stores the value, and then automatically sends it back in a Cookieheader on every future request that matches the cookie’s scope. Login sessions, shopping carts, language preferences, analytics — most of the web’s “state” rides on these small strings.
The catch is that a session cookie is the session. Steal it, and you are logged in as the victim without ever knowing their password. That is why cookies carry security attributes likeSecure, HttpOnly and SameSite. This guide walks through how cookies actually travel, the types you will meet (session vs persistent, first-party vs third-party), which attack each attribute defends against, and a copy-paste recipe for a hardened session cookie.
How cookies travel: Set-Cookie and Cookie
The whole mechanism is two steps.
- Server to browser: the response carries a header like
Set-Cookie: session=abc123; Path=/— one name=value pair followed by semicolon-separated attributes. One cookie, oneSet-Cookieline. - Browser to server: on every later request whose host, path and security context match, the browser silently adds
Cookie: session=abc123. Only the value goes back; the attributes are never re-sent — they are rules the browser uses to decide when to send.
That word “silently” is both the convenience and the danger. Because cookies ride along even on requests the user never intended — say, one fired from a malicious page — attacks like CSRF become possible. To see exactly which cookies your site sets today and with which attributes, drop the URL into the cookie checker.
Session vs persistent, first-party vs third-party
By lifetime there are two kinds. A cookie with no Expires date and noMax-Age is a session cookie: it evaporates when the browser closes. Add either attribute and it becomes a persistent cookie, written to disk until that moment passes (Max-Agewins if both are present). A sensible pattern is to keep the login session short-lived — hours to days — and put any “remember me” token in a separate, longer-lived cookie.
By ownership, a first-party cookie is set by the site in your address bar, while a third-party cookie is set by some other domain embedded in the page — ad networks, analytics scripts, social widgets. Because third-party cookies enable tracking across unrelated sites, browsers have been phasing them out: Safari and Firefox block them by default, and while Chrome stepped back from full removal in favor of user choice, the industry direction is settled — do not build anything new that depends on them. Your own login and preference cookies are first-party, so they are unaffected.
The security attributes at a glance
| Attribute | What it does | What it defends against |
|---|---|---|
Secure | Sent over HTTPS only | Eavesdropping on plaintext HTTP hops |
HttpOnly | Invisible to JavaScript (document.cookie) | XSS payloads exfiltrating the cookie |
SameSite | Strict/Lax/None — whether cross-site requests carry the cookie | CSRF (forged requests riding the user’s session) |
Domain | If set, shared with all subdomains; if omitted, host-only | Narrower scope means less exposure — omitting is safer |
Path | Sent only for requests under this path | Reduces unnecessary sends (weak as a security boundary) |
__Host- prefix | Name starting with __Host- forces Secure + Path=/ and forbids Domain | Subdomains overwriting the cookie (session fixation tricks) |
The intuition for SameSite: Strict never sends the cookie on any request originating from another site — safest, but users arriving via an external link appear logged out on the first page. Lax makes one pragmatic exception for top-level GET navigations (typing the URL, clicking a normal link), which is why it is the right default for most session cookies.None sends the cookie everywhere, must be paired with Secure, and belongs only to genuinely cross-site cases like embedded checkout frames.
Attack-to-defense pairs and the recommended recipe
| Attack | How it works | Primary defense |
|---|---|---|
| XSS cookie theft | Injected script reads document.cookie and posts it to the attacker | HttpOnly (fix the XSS itself with escaping and CSP) |
| Network sniffing | Plaintext HTTP traffic on public Wi-Fi is captured, cookie copied | Secure (plus HSTS to eliminate HTTP entirely) |
| CSRF | A hostile page fires a forged request at your site from the victim’s browser — cookies come along for free | SameSite=Lax or stricter (keep CSRF tokens too) |
| Session fixation / cookie tossing | A compromised subdomain plants a cookie for the parent domain | __Host- prefix |
Unless you have a specific reason not to, every session cookie should look like this:
Set-Cookie: session=<random-token>; Path=/; Secure; HttpOnly; SameSite=Lax
For the stricter variant, simply rename it __Host-session. And since cookies are only one layer, verify that the headers which shut down XSS and downgrade attacks — CSP, HSTS and friends — are in place with the security headers checker.
Worked example: dissecting one Set-Cookie line
Here is a realistic production cookie, read attribute by attribute.
Set-Cookie: __Host-session=9f2c1e...; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=86400
__Host-session=9f2c1e...— name and value. The__Host-prefix makes the browser refuse the cookie unlessSecureis set,Pathis/andDomainis absent. The value must be an unguessable random token.Path=/— sent on every path of the site; mandatory under the prefix rules.Secure— travels only over HTTPS. Even if someone loads the site over plain HTTP, this cookie stays home.HttpOnly— no script on the page can read it, so even a successful XSS injection cannot exfiltrate the raw session token.SameSite=Lax— withheld from cross-site POSTs, iframes and image requests; sent when the user actively navigates over via a link.Max-Age=86400— a persistent cookie that self-destructs after 24 hours. Without this attribute it would be a session cookie, gone when the browser closes.
A cookie wearing all five of those attributes is the textbook-correct session cookie. For a live site, run the cookie checkerto list every cookie your responses set and spot missing flags, then make sure your framework’s session settings (the secure and httpOnly options most middleware exposes) are switched on explicitly rather than left to defaults.
Frequently asked questions
Why does clearing cookies log me out of websites?
Does HttpOnly fully protect against XSS?
Should I use SameSite=Lax or Strict?
Will third-party cookie blocking break login on my site?
Is it better to set or omit the Domain attribute?
Tools to use with this guide
Related guides
- Essential Security Headers Guide (CSP, HSTS, X-Frame-Options)The security headers you must set, what each does, recommended values and common mistakes.
- Fixing CORS Errors: Access-Control-Allow-Origin ExplainedWhy the browser throws CORS errors and how to fix each case — preflight, credentials and the wildcard trap.