← All guides
High Reported as: Cookie missing Secure / HttpOnly / SameSite

Cookie flags: Secure, HttpOnly, SameSite

Why a session cookie missing Secure is High while the same gap on an analytics cookie is Low, and how to set all three flags correctly.

Three attributes decide whether a cookie can be stolen, read by injected script, or replayed by another site. SecScan checks all three on every cookie in Set-Cookie.

The same missing flag means very different things depending on what the cookie holds, so the checks are scored separately. SecScan classifies cookies by name — session, token, jwt, sid, PHPSESSID and similar patterns are treated as session or auth cookies; everything else (UI-state flags, analytics IDs) is not.

Missing flagOn a session cookieOn everything else
SecureHighLow
HttpOnlyMediumInfo
SameSiteMediumInfo

A theme-preference cookie without HttpOnly is fine — the front end has to read it. A session cookie without HttpOnly means any XSS on your domain is an account takeover. Reporting both at the same severity would train you to ignore the finding.

What each flag does

Secure — the cookie is only ever sent over HTTPS. Without it, a single plain-HTTP request to your domain (a hardcoded http:// link, a captive portal, a stale bookmark) puts the session token on the wire in cleartext.

HttpOnly — JavaScript cannot read the cookie via document.cookie. This does not prevent XSS; it removes the easiest thing an XSS payload does with it.

SameSite — controls whether the cookie rides along on cross-site requests.

  • SameSite=Lax — sent on top-level navigations, not on cross-site subrequests. The right default for session cookies.
  • SameSite=Strict — never sent cross-site. Safest, but a user following a link from an email arrives logged out.
  • SameSite=None — always sent, and requires Secure. Only for cookies that genuinely need to work in a third-party context, such as an embedded widget.

Browsers now treat a cookie with no SameSite attribute as Lax, but relying on that default is fragile — the behaviour differs across browsers and versions, and it leaves your intent unstated.

Setting them

Set-Cookie: session=…; Secure; HttpOnly; SameSite=Lax; Path=/; Max-Age=86400

In Express:

res.cookie('session', token, {
  secure: true,
  httpOnly: true,
  sameSite: 'lax',
  path: '/',
  maxAge: 86_400_000,
});

Secure breaks local development over plain HTTP. Gate it on environment (secure: process.env.NODE_ENV === 'production') rather than turning it off everywhere and forgetting.

Cookie theft via injected script is the delivery mechanism a Content-Security-Policy is meant to contain. The two findings compound: no CSP plus no HttpOnly means one reflected XSS is a full session compromise.

Last reviewed 2026-09-07