Web Storage and cookies from JavaScript

localStorage, sessionStorage, and cookies all persist data in the browser, but they differ in exactly how long that data lives, whether it's sent to the server automatically, and how much of it can be stored — picking the wrong one for a given job is a real, common source of bugs.

Intermediate

4 min read

localStorage: persists indefinitely, until explicitly cleared

localStorage.setItem("theme", "dark");
localStorage.getItem("theme");  // "dark" — survives a tab close, a browser restart, even days later
localStorage.removeItem("theme");
localStorage.clear();            // removes EVERYTHING this origin has stored

localStorage persists data with no expiration at all — it survives closing the tab, closing the browser entirely, and restarting the computer, remaining until explicitly removed via code or the user manually clearing site data. Every value is stored as a string — localStorage.setItem("user", someObject) silently stores the string "[object Object]", not the actual object, which is why storing structured data requires JSON.stringify() on the way in and JSON.parse() on the way out.

sessionStorage: the same API, but scoped to one tab's lifetime

sessionStorage.setItem("draftText", "unsaved content...");
// Survives a page RELOAD within the same tab, but is GONE the moment
// this specific tab is closed — and a NEW tab (even to the same site)
// gets its own, separate sessionStorage, not a shared one

sessionStorage shares localStorage's exact API (setItem, getItem, removeItem) but with genuinely different persistence: it survives a page reload within the same tab, but is cleared the moment that specific tab closes, and — critically — each tab gets its own independent sessionStorage, even for the same site opened in two different tabs simultaneously. This makes it the right choice for data that should genuinely be scoped to one browsing session in one tab (an in-progress, unsaved form draft) rather than something meant to persist and be shared across every tab and every future visit.

Cookies: the one storage mechanism the SERVER can read automatically

document.cookie = "sessionId=abc123; max-age=3600; SameSite=Lax"; // sets ONE cookie — a genuinely awkward API
document.cookie; // returns ALL cookies as ONE string: "sessionId=abc123; theme=dark" — needs manual parsing

Cookies are fundamentally different from localStorage/sessionStorage in one critical way: they're automatically included in every HTTP request to their domain (recall this platform's Web Security domain's CSRF lesson, which depends entirely on exactly this automatic-attachment behavior) — genuinely useful for session tokens the server needs to read on every request, something localStorage structurally can't do, since its contents are never sent over the network at all. The JavaScript API for cookies (document.cookie) is real, but genuinely awkward: setting one cookie means writing a whole formatted string, and reading returns every cookie concatenated into one string that has to be manually parsed.

Storage limits and the real, practical consequence of hitting them

try {
  localStorage.setItem("bigData", hugeString);
} catch (err) {
  // QuotaExceededError — localStorage typically caps around 5-10MB PER ORIGIN,
  // varying by browser — genuinely small compared to what a database or
  // even a single uploaded file can hold
}

localStorage and sessionStorage both have a real, practical storage limit — typically around 5–10MB per origin, varying by browser — small enough that storing anything resembling real application data (not just settings and small cached values) will genuinely hit the limit and throw a QuotaExceededError. This is worth knowing explicitly: localStorage is the right tool for small, simple values (preferences, feature flags, small cached API responses) and structurally the wrong tool for anything approaching "a real, growing dataset" — that's what IndexedDB (a genuine, more capable browser database, beyond this lesson's scope) or the actual server-side database exist for.

Why none of these are appropriate for a genuine secret

localStorage, sessionStorage, and non-HttpOnly cookies are all
READABLE by any JavaScript running on the page — including a
successful XSS payload (this platform's Web Security domain's own
lesson). Storing a real secret (an API key meant to stay server-only)
in ANY of these means an XSS vulnerability anywhere on the page can
read and exfiltrate it directly

Every mechanism this lesson covers is readable by JavaScript running on the page — which means a successful XSS attack (covered directly in this platform's Web Security domain) can read anything stored in localStorage, sessionStorage, or a non-HttpOnly cookie and send it to an attacker. A real secret that the server alone needs belongs in an HttpOnly cookie specifically (which JavaScript genuinely cannot read at all, closing exactly this exposure) or, for true API secrets, kept entirely server-side and never sent to the browser in the first place.

Further reading

Check your understanding

A quick comprehension check — not tracked, not graded, just for you.

1. What's the key persistence difference between localStorage and sessionStorage?

2. Why are cookies the right tool for a session token the SERVER needs to read, when localStorage isn't?

3. Why is storing a real API secret in localStorage or a non-HttpOnly cookie a real security risk?