Facebook Pixel

How to Securely Store JWT in Cookies

Cookies can be safe or risky for JWT. Here is how to do it securely.

How to Securely Store JWT in Cookies

Storing JWT in cookies can be safe or risky, depending on how you configure the cookie. Here is how to do it securely.

The Right Cookie Flags

res.cookie('token', jwt, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  maxAge: 7 * 24 * 60 * 60 * 1000
});

httpOnly: true

Scripts cannot read the cookie. This protects against XSS-driven token theft. Without httpOnly, any script (including injected ones) can read the token.

secure: true

The cookie is only sent over HTTPS. Without secure, the cookie is sent over HTTP too, and a man-in-the-middle can read it. Always use HTTPS in production.

sameSite: 'lax' (or 'strict')

Controls when the cookie is sent on cross-site requests.

  • 'strict': cookie is never sent on cross-site requests. Strongest CSRF protection.
  • 'lax': cookie is sent on top-level navigations (e.g., clicking a link to your site). Good balance.
  • 'none': cookie is always sent (requires secure: true). Use only if you need cross-site requests (e.g., third-party embeds).

maxAge

Set the cookie's maxAge to match the JWT's expiry. If the JWT expires in 7 days, set maxAge to 7 days. Otherwise the cookie outlives the token, and the client sends an expired token.

Domain and Path

  • domain: defaults to the current domain. Set explicitly if you want subdomains to share the cookie.
  • path: defaults to '/'. Restrict to a specific path if needed.

Clearing the Cookie on Logout

res.clearCookie('token', {
  httpOnly: true,
  secure: true,
  sameSite: 'lax'
});

Pass the same options you used to set it. Otherwise the browser may not clear it.

CSRF and sameSite

With sameSite: 'lax' or 'strict', CSRF is largely handled because cross-site requests do not include the cookie. For 'none', you need CSRF tokens.

Common Mistakes

  • No httpOnly: scripts can read the token (XSS steals it).
  • No secure: token sent over HTTP (MITM steals it).
  • sameSite: 'none' without secure: browser rejects the cookie.
  • Cookie outlives the token: client sends expired tokens.
  • Not clearing with the same options: cookie is not actually cleared.

The Takeaway

Store JWT in a cookie with httpOnly: true (no script access), secure: true (HTTPS only), sameSite: 'lax' or 'strict' (CSRF protection), and maxAge matching the JWT expiry. Clear with the same options on logout. Common mistakes: no httpOnly, no secure, sameSite 'none' without secure, cookie outliving the token.

Set httpOnly: true (no script access), secure: true (HTTPS only), sameSite: 'lax' or 'strict' (CSRF protection), and maxAge matching the JWT expiry. Clear with the same options on logout.

Scripts cannot read the cookie. This protects against XSS-driven token theft. Without httpOnly, any script (including injected ones) can read the token and send it to an attacker.

The cookie is only sent over HTTPS. Without secure, the cookie is sent over HTTP too, and a man-in-the-middle can read it. Always use HTTPS in production.

Controls when the cookie is sent on cross-site requests. 'strict' never sends it cross-site (strongest CSRF protection). 'lax' sends it on top-level navigations (good balance). 'none' always sends it (requires secure: true; use only for third-party embeds).

If you set the cookie with httpOnly, secure, sameSite, you must clear it with the same options. Otherwise the browser may not match the cookie and it will not be cleared. The user stays logged in.

Ready to master Node.js completely?

Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.

Please Login.
Please Login.
Please Login.
Please Login.