OneWebDesk

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.

  1. 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, one Set-Cookie line.
  2. 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

AttributeWhat it doesWhat it defends against
SecureSent over HTTPS onlyEavesdropping on plaintext HTTP hops
HttpOnlyInvisible to JavaScript (document.cookie)XSS payloads exfiltrating the cookie
SameSiteStrict/Lax/None — whether cross-site requests carry the cookieCSRF (forged requests riding the user’s session)
DomainIf set, shared with all subdomains; if omitted, host-onlyNarrower scope means less exposure — omitting is safer
PathSent only for requests under this pathReduces unnecessary sends (weak as a security boundary)
__Host- prefixName starting with __Host- forces Secure + Path=/ and forbids DomainSubdomains 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

AttackHow it worksPrimary defense
XSS cookie theftInjected script reads document.cookie and posts it to the attackerHttpOnly (fix the XSS itself with escaping and CSP)
Network sniffingPlaintext HTTP traffic on public Wi-Fi is captured, cookie copiedSecure (plus HSTS to eliminate HTTP entirely)
CSRFA hostile page fires a forged request at your site from the victim’s browser — cookies come along for freeSameSite=Lax or stricter (keep CSRF tokens too)
Session fixation / cookie tossingA 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 unless Secure is set, Path is / and Domain is 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?
Your logged-in state lives in a session cookie the server issued at login. Once you delete it, the next request arrives without the session identifier, so the server treats you as a brand-new visitor and asks you to sign in again.
Does HttpOnly fully protect against XSS?
No. HttpOnly only stops injected scripts from reading the cookie's value. A script running on your page can still perform actions using the victim's session by sending requests directly. Treat HttpOnly as a damage limiter; the real XSS fixes are output escaping and a Content-Security-Policy.
Should I use SameSite=Lax or Strict?
Lax is the right choice for most login sessions. Strict drops the cookie even when a user follows an external link to your site, which makes them appear logged out on arrival, so it is usually reserved for a separate cookie guarding sensitive actions like fund transfers. None should be used only for genuine cross-site embeds, and always together with Secure.
Will third-party cookie blocking break login on my site?
No. Login, cart and preference cookies are first-party — set by the site the user is actually visiting — and are not targeted by the phase-outs. What breaks is cross-site tracking and cookies needed inside cross-site iframes, such as embedded widgets that require their own session.
Is it better to set or omit the Domain attribute?
Omit it unless you truly need sharing. Without Domain, the cookie is sent only to the exact host that set it. Setting Domain=example.com broadcasts it to every subdomain, widening the attack surface. Only specify it for legitimate cross-subdomain needs like single sign-on — and note that __Host- prefixed cookies forbid Domain entirely.

Tools to use with this guide

Related guides