Browser Storage Comparison
Cookies, localStorage, sessionStorage, IndexedDB, Cache Storage — capacity, persistence, and when each is the wrong tool.
- browser
- storage
- localStorage
- indexeddb
- cookies
Picking browser storage is an API choice and a security/product choice. Putting session tokens in localStorage “because it’s easy” is a classic XSS footgun. Using cookies for megabytes of offline data is the opposite mistake.
Docs: MDN Client-side storage, web.dev Storage.
Comparison table
| API | Typical size | Persistence | JS access | Sent to server |
|---|---|---|---|---|
| Cookies | ~4KB each | Configurable | Unless HttpOnly |
Yes (matching requests) |
| localStorage | ~5MB / origin | Until cleared | Yes (sync) | No |
| sessionStorage | ~5MB / origin | Tab/session | Yes (sync) | No |
| IndexedDB | Large (quota) | Until cleared | Async | No |
| Cache Storage | Large (quota) | Until cleared | Async (Request/Response) | No |
| Memory only | RAM | Page lifetime | Yes | No |
Quotas and eviction policies vary by browser; treat “5MB” as a rule of thumb, not a contract.
Cookies
Best for small auth session identifiers the server must see.
Set-Cookie: session=abc; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=86400
HttpOnly— JS cannot read (mitigates token theft via XSS).Secure— HTTPS only.SameSite— CSRF posture (see Secure cookie flags).
Do not stuff JWTs with PII into giant cookies. Watch total header size.
localStorage / sessionStorage
localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme');
Sync API — large reads/writes on the main thread can jank. No structured clone of complex graphs beyond JSON. Any XSS can read it — never store long-lived access tokens here if you can use HttpOnly cookies instead.
sessionStorage is per-tab: good for wizard draft state that shouldn’t leap tabs.
IndexedDB
Async, transactional, large structured data (offline apps, drafts, media metadata).
// sketch with idb-style API mental model
const tx = db.transaction('notes', 'readwrite');
await tx.objectStore('notes').put({ id: '1', body: text });
Use for offline-first product data, not for session secrets.
Cache Storage
Part of the service worker ecosystem: store Request/Response pairs for offline shells and asset precache — Cache Storage API.
Decision cheat sheet
- Auth session for same-site API →
HttpOnly+Secure+SameSitecookie. - User preference (theme) →
localStorageor cookie if SSR needs it. - Multi-MB offline data → IndexedDB.
- App shell / offline assets → Cache Storage + SW.
- Ephemeral tab state →
sessionStorageor memory.
Footguns
- Storage is origin-scoped (scheme+host+port).
- Private mode and user settings can clear data aggressively.
localStoragethrows in some blocked-storage contexts — wrap writes.- Quota errors on IDB/Cache need user-facing handling.
Interview out-loud
“Cookies for small server-sent session IDs with HttpOnly Secure SameSite. localStorage for non-sensitive prefs. IndexedDB for large structured offline data. Cache Storage for Request/Response caching with service workers. Never put access tokens in localStorage if XSS is in your threat model.”
Security x storage matrix
| Data | Store |
|---|---|
| Session id | HttpOnly cookie |
| Theme preference | localStorage or cookie if SSR needs it |
| Offline drafts | IndexedDB |
| Access token (prefer not) | Memory only, short TTL |
| App shell assets | Cache Storage |
If product insists on “remember me” for months, the refresh capability must still be revocable server-side. Storage choice doesn’t replace server session hygiene.
Related
Further reading
Related guides
- BFCache Back Forward CacheHow the back/forward cache freezes pages for instant history nav, what blocks it, and how to restore state safely.
- Browser DevTools Network PanelRead waterfalls, timing phases, headers, throttling, and initiator chains in the Network panel like a production debugger.
- Browser DevTools Performance PanelRecord main-thread timelines: long tasks, style/layout/paint, frames, and how to turn flame charts into INP fixes.
- Browser Extension Messaging BasicsHow content scripts, background/service workers, and pages talk: runtime messaging, ports, and security boundaries.
- Browser Networking 101DNS, TCP/TLS, HTTP/1.1 vs H2/H3, connection reuse, and what frontend code can actually influence.