DEV3 Technologies home

DEV3 Developer Forum

Where should I store a JWT in a JavaScript web app: localStorage or cookies?

JavaScript helpSolved0 views
dev3 ·

My SPA stores the JWT in localStorage and attaches it as a Bearer token. A colleague says that is insecure. What is the safer approach and what are the trade-offs?

#javascript#security#jwt#cookies

0

1 reply

dev3 · Accepted answer

The risk with localStorage is XSS: any injected script (a compromised npm package, an unsanitised comment, a third-party widget) can read it and send it to an attacker. The token then works from anywhere until it expires.

Safer default: HttpOnly cookie

The server sets the session in a cookie that JavaScript cannot read:

Set-Cookie: session=...; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600
  • HttpOnly: not readable by scripts, so XSS cannot steal it (it can still make requests as the user while the page is open).
  • Secure: HTTPS only.
  • SameSite=Lax (or Strict): blocks most cross-site request forgery. For sensitive state-changing endpoints also use CSRF tokens or check the Origin header.

Client code then just calls fetch('/api/me', { credentials: 'include' }) if the API is on another origin (with proper CORS and SameSite=None; Secure where necessary).

Trade-offs

localStorage HttpOnly cookie
XSS steals token yes no
CSRF risk no (manual header) yes, mitigate with SameSite
Works with mobile/other clients easy needs cookie handling
SSR can read it no yes

Good practices either way

  1. Short-lived access tokens (minutes) plus a refresh token in an HttpOnly cookie, rotated on use.
  2. Content-Security-Policy to limit script sources.
  3. Escape output and sanitise any HTML (use a sanitiser, avoid innerHTML).
  4. Keep dependencies audited.
  5. Use server-side sessions or a library (Auth.js or your framework's built-in session support) instead of inventing token handling.

Inspect token contents with the JWT decoder, and remember a JWT payload is only encoded, not encrypted.

Pitfall: storing a token in a normal (non-HttpOnly) cookie has the same XSS exposure as localStorage.

0

Your reply

Every post is reviewed by DEV3 before it appears. Usually reviewed within 24 hours.

Markdown supported. Use ``` for code blocks. Images are not allowed.