ESC

Type to search the knowledge base.

Cookies for Frontend Engineers

document.cookie, HttpOnly, Secure, SameSite, and when cookies beat localStorage for auth — practical rules without backend myths.

intermediate3 min read
  • javascript
  • cookies
  • auth
  • security

Cookies are small key/value pairs the browser stores per domain and automatically sends on matching requests. That auto-send is why they still win for session auth — and why misconfigured cookies become CSRF and XSS disaster fuel.

Frontend engineers don’t set server cookie policy alone, but you must read document.cookie, know what JS cannot see, and pick SameSite correctly when you own the client.

document.cookie is a weird string

// read all non-HttpOnly cookies for this document
console.log(document.cookie);
// "theme=dark; locale=en"

// write (one cookie per assignment)
document.cookie = `theme=dark; path=/; max-age=${60 * 60 * 24 * 365}; samesite=lax`;

// delete: expire in the past
document.cookie = 'theme=; path=/; max-age=0';

There is no structured API in the platform for listing cookies with attributes. You parse the string yourself:

function getCookie(name) {
  const prefix = `${encodeURIComponent(name)}=`;
  return document.cookie
    .split('; ')
    .find((row) => row.startsWith(prefix))
    ?.slice(prefix.length);
}

Attributes that matter

Attribute Why you care
HttpOnly JS cannot read — mitigates token theft via XSS
Secure Only sent over HTTPS
SameSite=Lax/Strict/None Cross-site send rules (CSRF)
Path / Domain Scope of where it’s sent
Max-Age / Expires Lifetime
Partitioned CHIPS — third-party cookie partitioning
// client-set cookie for UI prefs only — never session secrets
document.cookie =
  'sidebar=collapsed; path=/; max-age=31536000; samesite=lax; secure';

Auth tokens in localStorage are readable by any XSS script. HttpOnly cookies are not. That is the usual argument for cookie-based sessions — paired with CSRF defenses.

SameSite in practice

  • Strict — never sent on cross-site navigations (can break OAuth return links).
  • Lax — sent on top-level GET navigations; good default for many session cookies.
  • None — cross-site; requires Secure. Needed for some embeds/third-party flows.
Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Lax; Path=/

What frontend can and cannot do

// can: theme, locale, non-sensitive UI state
// cannot: read HttpOnly session cookie
// should not: store long-lived access tokens in JS-visible cookies without threat modeling

fetch / XHR send same-origin cookies by default. Cross-origin needs credentials: 'include' and server Access-Control-Allow-Credentials + explicit origin (not *).

await fetch('https://api.example.com/me', {
  credentials: 'include',
});

Size and count limits

Browsers roughly allow ~4KB per cookie and a few hundred per domain. Don’t stuff JWTs with full profile graphs into cookies — keep sessions server-side or use short opaque IDs.

Interview answer

“Cookies are sent automatically on matching requests. document.cookie only sees non-HttpOnly cookies and uses a string API. For auth I prefer HttpOnly+Secure+SameSite session cookies set by the server, with CSRF strategy as needed. localStorage is fine for non-secrets; XSS can steal anything JS can read.”

Prefixes and third-party sunset

Set-Cookie: __Host-session=abc; Path=/; Secure; HttpOnly; SameSite=Lax

__Host- requires Secure, no Domain, Path=/ — hardest to mis-scope. __Secure- requires Secure. Prefer these prefixes for session cookies when you control the server.

Third-party cookies are being retired in major browsers. Embeds that relied on third-party session cookies need Storage Access API, first-party proxies, or partitioned cookies (Partitioned). As a frontend engineer, don’t design new features that require third-party cookie reads from JS (document.cookie never saw HttpOnly ones anyway, and cross-site readable cookies are increasingly blocked).

Further reading

Related guides