A session cookie is a bearer credential: whoever holds it is treated as the logged-in user until it expires. That makes cookie configuration one of the most consequential security decisions in a web application, and also one of the most quietly neglected. The defaults are permissive, the flags are easy to forget, and a single missing attribute can turn an otherwise solid authentication system into one where a cross-site script reads the session token or a forged cross-site request rides on it. Getting cookies right is cheap, and getting them wrong is how account takeovers happen.
The short answer for 2026: set HttpOnly, Secure, and an appropriate SameSite value on every session cookie, use the __Host- prefix to lock scope, keep cookies small and never store sensitive data in them directly, and keep your cookie-parsing library patched. Here is how each piece works.
The three essential flags
HttpOnly hides the cookie from JavaScript, so a cross-site scripting bug cannot read your session token through document.cookie. Secure ensures the cookie is only ever sent over HTTPS, closing the window where it could leak over plaintext. SameSite controls whether the cookie is attached to cross-site requests, which is the primary defense against cross-site request forgery.
Set-Cookie: session=eyJ...; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600
Choose SameSite deliberately. Lax is the sensible default for session cookies: the cookie rides along with top-level navigations to your site but not with cross-site subresource requests or background POSTs. Use Strict for the most sensitive cookies where even a followed link should not carry the session. Use None only when you genuinely need the cookie in a cross-site context, and it then requires Secure.
In an application framework the same settings look like this:
// Express: a hardened session cookie
res.cookie("session", token, {
httpOnly: true,
secure: true,
sameSite: "lax",
path: "/",
maxAge: 3600_000,
});
Lock scope with cookie prefixes
Attributes alone can be undermined by a related-domain or network attacker who overwrites your cookie with a more loosely scoped one. Cookie name prefixes let the browser enforce scope for you. A cookie named with the __Host- prefix is only accepted if it is Secure, has no Domain attribute (so it is bound to the exact host), and has Path=/. That combination prevents a subdomain from setting or clobbering it.
Set-Cookie: __Host-session=eyJ...; HttpOnly; Secure; SameSite=Lax; Path=/
Prefer __Host- for session cookies whenever your architecture does not require sharing the cookie across subdomains. It is the strongest scoping guarantee the platform offers, and it is free.
Keep the payload safe and small
Do not store sensitive data directly in a cookie. A cookie should carry an opaque, high-entropy session identifier that maps to server-side state, or a signed and (if it contains anything private) encrypted token, never a raw user ID, role, or personal data an attacker could read or tamper with. Keep cookies small, because they are sent on every matching request, and set an explicit Max-Age so sessions expire rather than living indefinitely.
Parsing matters too. CVE-2024-47764 was a flaw in the widely used cookie npm package (fixed in 0.7.0) where out-of-bounds characters in a cookie's name, path, or domain were not properly rejected, allowing cookie fields to be manipulated. Because that package sits under countless frameworks transitively, a stale copy is a real risk, and it is exactly the kind of deep dependency teams forget to upgrade.
For cross-site contexts, use CHIPS
Third-party cookies are being phased down, and embedded contexts that legitimately need state should use partitioned cookies (CHIPS). Adding the Partitioned attribute binds the cookie to the top-level site it was set under, so it cannot be used for cross-site tracking while still working for the embed that needs it.
Set-Cookie: __Host-embed=abc; HttpOnly; Secure; SameSite=None; Path=/; Partitioned
Cookie hardening checklist
| Setting | Use it for |
|---|---|
HttpOnly | Every session cookie, to block JS theft via XSS |
Secure | Every cookie, to prevent plaintext leakage |
SameSite=Lax (or Strict) | CSRF defense on session cookies |
__Host- prefix | Locking a cookie to the exact host with Path=/ |
| Opaque high-entropy value | Never storing readable sensitive data |
Explicit Max-Age | Bounding session lifetime |
Partitioned (CHIPS) | Legitimate cross-site embedded state |
| Patched cookie parser | Avoiding CVE-2024-47764-class parsing flaws |
How Safeguard helps
Cookie flags are a runtime behavior of your responses, and the parsing library behind them is a dependency, so both are in scope for Safeguard. Dynamic application security testing inspects the Set-Cookie headers your live application emits and flags missing HttpOnly, Secure, or SameSite attributes. Software composition analysis tracks the cookie-handling libraries in your graph, so a vulnerable version like the pre-0.7.0 cookie package (CVE-2024-47764) is surfaced with reachability context, and autonomous auto-fix opens a tested pull request to bump it. Griffin AI explains the exposure and the correct settings for your framework.
Stop leaking and losing sessions: get started free or read the documentation.
Frequently Asked Questions
What is the difference between HttpOnly, Secure, and SameSite?
HttpOnly prevents JavaScript from reading the cookie, defending against theft via cross-site scripting. Secure ensures the cookie is only sent over HTTPS, preventing plaintext leakage. SameSite controls whether the cookie is attached to cross-site requests, which is the main defense against cross-site request forgery. Session cookies should set all three.
Which SameSite value should I use for session cookies?
Lax is the right default: the cookie accompanies top-level navigations to your site but not cross-site subresource requests or background POSTs, which blocks most CSRF while keeping normal links working. Use Strict for the most sensitive cookies, and None (which requires Secure) only when a cross-site context genuinely needs the cookie.
What does the __Host- cookie prefix do?
It makes the browser enforce strict scoping: a __Host- cookie is only accepted when it is Secure, has Path=/, and has no Domain attribute, binding it to the exact host. This prevents a subdomain or related-domain attacker from setting or overwriting the cookie, so it is the strongest free scoping guarantee for session cookies.
Can a vulnerable cookie library really matter?
Yes. CVE-2024-47764 in the cookie npm package allowed out-of-bounds characters in cookie name, path, and domain fields, enabling field manipulation, and it was fixed in 0.7.0. Because that package is a deep transitive dependency of many frameworks, teams often run a stale, vulnerable copy without realizing it, so keep it patched.