Key takeaway
- Cookies, local storage and session storage compared on lifetime, size, scope and security, with a decision table and security rules for web teams.
Cookies, local storage and session storage all keep data in the browser, but they differ in lifetime, scope, size and whether the server sees the data. Use cookies for small values the server must read on every request, such as a session reference. Use local storage for non-sensitive preferences that should survive a restart, and session storage for data that should vanish when the tab closes.
The rule that matters most for enterprise teams is that none of the three is a safe place for secrets. Anything JavaScript can read, a cross-site scripting flaw can steal, so authentication tokens belong in cookies with the HttpOnly attribute, and sensitive records belong on the server. This guide sets out the differences, the security trade-offs and a decision rule, with every figure taken from MDN and OWASP. For the JavaScript side, see our guide to the module pattern in JavaScript.
What is the difference between cookies, local storage and session storage?
Cookies are small name-value pairs that the browser sends back to the server with matching requests. Local storage and session storage belong to the Web Storage API: they are read and written by JavaScript and are never sent to the server automatically. The table below compares them on the points that drive the decision.
| Property | Cookies | Local storage | Session storage |
|---|---|---|---|
| Sent to the server | Yes, usually with every matching request | No | No |
| Lifetime | Session cookie until the session ends; persistent cookie until Expires or Max-Age | Persists after the browser is closed and reopened | Destroyed when the tab is closed |
| Scope | Domain and path, set by the Domain and Path attributes | One origin, shared by all tabs of that origin | One origin and one tab |
| Size | Usually 4 KB per cookie, with a per-domain count limit | 5 MiB per origin | 5 MiB per origin |
| Readable by JavaScript | Yes, unless the HttpOnly attribute is set | Always | Always |
| Value type | Text | Strings only | Strings only |
| Best fit | Session reference the server must read | Non-sensitive preferences and cached UI state | Short-lived, per-tab state such as a draft step |
Sources: MDN, Using HTTP cookies, Web Storage API and Storage quotas and eviction criteria. Read 13 November 2026. Quotas and cookie limits vary by browser, so test on the browsers you support.
How do cookies work?
A server sets a cookie with a Set-Cookie header. The browser then sends stored cookies for that domain back in a Cookie header on later requests. According to MDN, cookies are sent with every request, so many or large cookies can worsen performance, especially on slow mobile connections.
Session cookies and persistent cookies
A session cookie has no Max-Age or Expires attribute and is deleted when the session ends. MDN notes that the browser decides when the session ends, and session restoring can make session cookies last far longer than expected. A persistent cookie carries Expires or Max-Age and is deleted after that date or period; Max-Age takes precedence when both are set.
Attributes that control cookie security
| Attribute | What it does | Why it matters |
|---|---|---|
| HttpOnly | Stops JavaScript reading the cookie through document.cookie | Helps limit cookie theft through cross-site scripting |
| Secure | Sends the cookie only over HTTPS | Keeps it off unencrypted connections |
| SameSite | Strict, Lax or None controls when the cookie is sent on cross-site requests; Lax is the default when unset | Reduces cross-site request risk; None requires Secure |
| Domain and Path | Limit which hosts and URL paths receive the cookie | Path is not a security boundary, according to MDN |
MDN also warns that cookie values are visible to, and can be changed by, the end user by default, so never trust a cookie value without checking it on the server.
How does local storage work?
Local storage keeps string key-value pairs per origin. MDN states that it persists even when the browser is closed and reopened, and that all documents from the same origin share the same area. The core calls are localStorage.setItem(key, value), getItem(key), removeItem(key) and clear().
Because values must be strings, convert objects with JSON.stringify() before storing and JSON.parse() after reading. MDN shows that storing a plain object without conversion gives the useless string "[object Object]". MDN also states that browsers allow 5 MiB of local storage per origin and throw a QuotaExceededError when the limit is reached, so wrap writes in try...catch.
Strengths and limits
| Strengths | Limits |
|---|---|
| Persists across restarts without an expiry date | Always readable by any script on the origin, including injected code |
| Never sent to the server, so no request overhead | Synchronous, so large writes can block the main thread, according to MDN |
| Simple API with no server work | No expiry control; no way to restrict visibility to a path, according to OWASP |
How does session storage work?
Session storage has the same API as local storage but is partitioned by tab as well as origin. MDN states that closing the tab destroys its session storage. Two tabs on the same site therefore see separate session storage, which suits per-tab state such as a multi-step form position. The same setItem() and getItem() calls apply, and the same 5 MiB per-origin quota applies according to MDN.
In private browsing, MDN notes that local storage behaves like session storage, with data deleted when the window or tab closes. Do not build features that rely on local storage surviving in private windows.
Which one is safe for tokens and personal data?
OWASP advises avoiding sensitive information in local storage where authentication would be assumed, and says not to store session identifiers there because the data is always accessible by JavaScript. It recommends session storage instead of local storage when persistence is not needed. A single cross-site scripting flaw can be used to steal everything in these objects, and OWASP also says not to treat their contents as trusted, because injected script can write to them too.
| Data | Suggested place | Reason |
|---|---|---|
| Session identifier or auth token | Cookie with HttpOnly, Secure and a suitable SameSite value | JavaScript cannot read an HttpOnly cookie, according to MDN |
| Theme, language, layout preference | Local storage | Non-sensitive and useful after restart |
| Wizard step or unsaved draft state | Session storage | Disappears with the tab |
| Personal or regulated records | Server side, under access control | Browser storage is user-editable and script-readable |
| Large offline datasets | IndexedDB or the Cache API | Larger quotas and asynchronous access, according to MDN |
Browser storage is only one layer. Pair it with response headers that limit script injection; our guide to HTTP security headers covers Content Security Policy and related controls, and the zero trust security architecture guide explains why the server should re-check every request rather than trust client state.
What about third-party cookies and privacy?
MDN reports that browser vendors have begun blocking third-party cookies by default or have announced plans to, and that Web Storage access from third-party iframes is denied if the user has disabled third-party cookies. Treat any design that depends on cross-site storage as fragile. Consent and retention duties for cookies and similar technologies depend on your jurisdiction, so confirm them with your privacy counsel and the official guidance of your regulator; this guide does not give legal advice.
When should you not use browser storage?
Avoid browser storage when the data is sensitive, when it must be consistent across devices, or when the server must enforce it. A cart total, a permission or an approval state held only in the browser can be edited by the user. Keep the authoritative copy on the server and use the browser only as a cache. Storage and quota also differ by browser and by private mode, so never assume a write succeeded without handling the error.
How do we choose in practice?
| Step | Question | Action |
|---|---|---|
| 1 | Does the server need this value on each request? | If yes, use a cookie with Secure, HttpOnly where possible, and a suitable SameSite value |
| 2 | Is the data sensitive or regulated? | Keep it on the server, not in any browser store |
| 3 | Should it survive closing the browser? | Local storage if non-sensitive; otherwise session storage |
| 4 | Is it larger than a few MiB or structured? | Use IndexedDB |
| 5 | Could an injected script abuse it? | Add a Content Security Policy and test write failures |
For enterprises running AI agents in browser-based workflows, the same rule applies: governed state belongs server side. ElixirData Context OS keeps governed context, versioned policy and decision traces in one decision layer above AI agents, and AgentGRC keeps autonomous agent actions inside policy, with the audit trail risk teams need.
To map one browser-side data decision your agents should make, and the context and policy it depends on, book a Context Workshop. See what happens in the workshop.
Frequently asked questions
What is the main difference between local storage and session storage?
Lifetime and scope. According to MDN, local storage persists after the browser is closed and is shared by all tabs of an origin, while session storage is partitioned by tab and destroyed when the tab closes.
How much data can each one hold?
MDN states that browsers allow 5 MiB of local storage and 5 MiB of session storage per origin, and that cookies are usually limited to 4 KB each. Limits vary by browser.
Are cookies or local storage more secure?
Neither is secure by default. A cookie with the HttpOnly attribute cannot be read by JavaScript, which is why OWASP advises against keeping session identifiers in local storage, where any injected script can read them.
Are cookies sent to the server?
Yes. The browser usually sends stored cookies for the domain with each matching request. Local storage and session storage are never sent automatically.
Can I store objects in local storage?
Only as strings. MDN says Storage supports only strings, so convert objects with JSON.stringify() before saving and JSON.parse() after reading.
When should I use IndexedDB instead?
Use IndexedDB when you need larger or structured data or asynchronous access, because Web Storage is synchronous and capped at 5 MiB per origin, according to MDN.
Take the next step with cookies and storage solutions
Talk to our experts about implementing effective cookie management strategies, and how industries and different departments leverage local storage and session storage to enhance web application performance. Utilize these technologies to optimize data storage, improve user experience, and ensure seamless and efficient access across sessions.
Ready to put AI agents to work? See an ElixirClaw demo: the agentic OS to build, orchestrate and govern AI agents across your enterprise systems.
Explore the XenonStack platform family: ElixirData · ElixirClaw · ElixirHub · NexaStack · MetaSecure · Neural AI · AgentGRC · AgentSRE · AgentAnalyst · AgentInstruct · AgentRAI · AgentScrum · AgentSearch · AgentForce · AI Aviator · SAIF Aviator · CohortIQ · XenonStack AI · XenonStack UAE