August 23, 2026
Cookies vs Sessions vs Local Storage: What Goes Where
One 40-byte cookie — not your password — is why you’re still logged in tomorrow, and knowing what belongs in a cookie, on the server, or in…

By BreakingCode
5 min read
One 40-byte cookie — not your password — is why you're still logged in tomorrow, and knowing what belongs in a cookie, on the server, or in local storage is the difference between a fast site and a stolen account.
Close your laptop tonight and open the shop tomorrow: still logged in. Your password wasn't saved anywhere — the browser sent it once and forgot it. What survived the night is a cookie about 40 bytes long, holding a random ID and nothing else.
That cookie is one of three places a website can keep data about you: cookies (in the browser, sent to the server), sessions (on the server), and local storage (in the browser, kept there). Developers mix them up constantly, and the mix-ups range from wasted bandwidth to stolen accounts. This article follows one login through all three and ends with a two-question rule for deciding what goes where.
Cookies: the browser's automatic passenger
Start with the login. Sam types a password into shop.com and the browser sends it once. The server checks it and replies with one extra response header:
HTTP/1.1 200 OK
Set-Cookie: sid=8f3a2c91d4e0; Path=/; Max-Age=2592000; HttpOnly; Secure; SameSite=LaxHTTP/1.1 200 OK
Set-Cookie: sid=8f3a2c91d4e0; Path=/; Max-Age=2592000; HttpOnly; Secure; SameSite=LaxInside is a random ID — sid=8f3a2c91…. Not Sam's name, not the password, not an email. Just a string the server made up.
The browser files that cookie under the site's domain. From then on it attaches the cookie to every single request to shop.com — every page, every image, every API call — without any code asking it to:
GET /cart HTTP/1.1
Host: shop.com
Cookie: sid=8f3a2c91d4e0GET /cart HTTP/1.1
Host: shop.com
Cookie: sid=8f3a2c91d4e0That is the defining trait of a cookie: it is the one thing your browser sends without being asked. It is also why cookies stay tiny — browsers cap each one at about 4 KB, and the whole jar for a domain rides on every request. Ten large cookies would tax every image on the page.
Two flags decide how long it lives. Without an expiry, the cookie is a session cookie: gone when the browser closes. Add Max-Age (or Expires) and it survives restarts. That thirty-day Max-Age in the header above is tomorrow's login, right there.
Open DevTools on any real site and look at the jar. GitHub's session cookie, _gh_sess, is a long random string — nothing about who you are is in it. Which raises the obvious question: if the cookie only holds an ID, where does the actual "you" live?
Sessions: the truth stays on the server
On the server. When shop.com minted that cookie it also wrote a matching row in a table: 8f3a… → user 7, Sam, cart has two items. That table is the session store — server memory for a toy app, or a fast key-value store like Redis for anything real:
SESSIONS
8f3a2c91d4e0 → { user: 7, name: "Sam", cart: [1042, 2210], role: "customer" }SESSIONS
8f3a2c91d4e0 → { user: 7, name: "Sam", cart: [1042, 2210], role: "customer" }
Every request now works like a coat check. The browser hands over the ticket (the cookie); the server looks it up in the rack and knows: that's Sam, two items in the cart. The ticket is worthless without the rack.
The point of a session is that your real data never leaves the server. Want to edit your cart total from the browser? You can't — the cart isn't in the browser. Log out? The server deletes the row and the cookie becomes a key to nothing.
One more lock. Mark the cookie HttpOnly and page scripts can't read it at all — document.cookie won't even list it. So a malicious script injected into the page (a cross-site-scripting bug, a compromised third-party widget) can't steal the ticket. Add Secure and the browser only sends it over HTTPS.
But that's identity. What about Sam's dark-mode setting? The server doesn't need it on every request; sending "theme: dark" two hundred times a day is pure waste. It needs a home that isn't the cookie jar.
Local storage: the browser's own drawer
That home is local storage: a key-value store inside the browser, controlled by JavaScript, that never goes to the server unless your code explicitly sends it. One line saves a setting, one line reads it back:
localStorage.setItem('theme', 'dark');
localStorage.getItem('theme'); // "dark"
localStorage.removeItem('theme');localStorage.setItem('theme', 'dark');
localStorage.getItem('theme'); // "dark"
localStorage.removeItem('theme');It's scoped per site (per origin), it survives browser restarts, and it's roomy — about 5 MB per site, over a thousand cookies' worth. It costs the server nothing because the server never sees it.
Here's a real one. Open DevTools → Application → Local Storage on youtube.com: YouTube keeps your player quality and volume there (yt-player-quality, yt-player-volume). Settings only the page cares about, and the server never needs.
Two catches. First, any script running on the page can read local storage — there is no HttpOnly equivalent — so a token that unlocks your account does not belong there. Second, it's per browser: set dark mode on your laptop and your phone knows nothing about it.
There's a sibling: sessionStorage. Same API, but scoped to one tab and wiped when the tab closes — a good fit for a half-finished checkout form or a multi-step wizard.
sessionStorage.setItem('checkout-step', '2'); // this tab only; gone when the tab closessessionStorage.setItem('checkout-step', '2'); // this tab only; gone when the tab closes
What goes where: two questions
Three places, three personalities. To sort a piece of data, ask two questions:
- Does the server need it on every request?
- Would it hurt if a script stole it?
The answers map straight onto the containers:
- Server needs it, and it's sensitive — the session ID, the login. That's a cookie flagged
HttpOnlyandSecure, and the real data (user, cart, role) sits in the server-side session. - Server needs it, but it's harmless — your language, a cookie-consent flag. A small plain cookie; no lock needed.
- Only the page needs it, and it isn't secret — theme, a draft post, a cached list. Local storage. Per tab?
sessionStorage.
And the password? None of the above. Ever. The browser sends it once at login and forgets it — that is the whole reason the session cookie exists. If you find yourself storing a password, or a long-lived API token, in localStorage "so the user stays logged in", stop: that's the job of an HttpOnly cookie plus a server session.
- Cookies live in the browser and ride on every request to their site, automatically — keep them tiny (≈4 KB) and put only what the server needs there.
- Sessions live on the server (memory/Redis) and hold the truth — user, cart, role. The cookie is just the key; logout deletes the row.
- Flag the session cookie
HttpOnly+Secureso page scripts can't read it and it never travels over plain HTTP. - localStorage lives in the browser only, ≈5 MB per site, readable by any script — for page-only, non-secret data (theme, drafts, cache).
**sessionStorage**is the per-tab version. - Two questions sort everything: does the server need it on every request? and would it hurt if a script stole it?
- The password goes nowhere. It's sent once and forgotten; the session cookie exists so it never has to be sent again.
Watch the animated version: