flawopen.com/Reference/JWT vs. HttpOnly Cookies
JWT in localStorage vs. HttpOnly cookies: which is safer?
For many browser applications, a server-managed session delivered in an HttpOnly cookie is a strong default. JavaScript cannot read that cookie, which limits token extraction, but it does not neutralize XSS: injected same-origin code may still make authenticated requests and read data available to the page.
The Security Trade-Offs
| Storage / transport | XSS exposure | CSRF behavior | Revocation |
|---|---|---|---|
localStorage bearer token | Same-origin JavaScript can read it; XSS can steal it or act as the user. | Not attached automatically to cross-site requests; XSS and client-side CSRF remain risks. | A self-contained JWT may remain valid until exp unless revocation is checked. |
HttpOnly cookie | JavaScript cannot read the cookie value; XSS can still issue authenticated requests. | Sent automatically under cookie rules; SameSite is only one CSRF layer. | An opaque server session can be invalidated server-side; a JWT cookie needs its own revocation policy. |
Recommended Architecture
- ✓Store session identifiers in
HttpOnly; Secure; SameSite=Laxcookies. - ✓Avoid storing persistent authentication tokens in
localStorageorsessionStorage.
Separate token format, storage, and session state
A JWT is a token format; localStorage and cookies are browser storage and delivery mechanisms. A JWT can be stored in either. An opaque session identifier in a cookie usually points to server-managed session state. “JWT vs cookie” is not a direct either-or comparison.
What each choice changes
The browser does not automatically attach localStorage values to cross-site requests, but same-origin XSS can read a stored bearer token or act through the application. Cookies are attached automatically under browser rules; HttpOnly hides the value from JavaScript but does not stop injected code from using the signed-in page. SameSite=Lax is one CSRF layer, not a complete guarantee: safe-method top-level navigations and same-site requests still matter. Keep state changes off GET and use an appropriate CSRF defense, such as validated tokens and/or Origin checks.
Revocation depends on server state and token validation, not storage location. A server-side session can be invalidated by deleting its record. A self-contained JWT may remain accepted until expiry unless the verifier checks revocation; short lifetimes reduce but do not remove that window.
Practical starting point
- ✓For a first-party web app, prefer an opaque server-side session or a Backend-for-Frontend (BFF) when it fits.
- ✓Set session cookies with
Secure,HttpOnly, an intentionalSameSitevalue,Path=/, and noDomainwhere possible; consider__Host-. - ✓Use CSRF tokens and/or Origin validation for state-changing requests as required by the threat model; never mutate state through
GET. - ✓If browser-held JWTs are required, use short lifetimes, define logout/revocation, and prevent XSS. Cookie storage does not replace authorization checks.