privacy

Secure cookie attributes

MetricSpot inspects Set-Cookie headers for Secure, HttpOnly, and SameSite. Missing any of these widens the attack surface for cookie theft and CSRF.

What this check does

Reads every Set-Cookie response header and checks for three flags:

  • Secure — cookie only sent over HTTPS.
  • HttpOnly — cookie not readable from JavaScript (blocks XSS-driven theft).
  • SameSite=Lax|Strict|None — cross-site request behavior (blocks CSRF).

Passes when every cookie carrying authentication or session state has all three.

Why it matters

Cookies hold the keys to your users’ sessions. A cookie without these flags is one XSS vulnerability or one hostile Wi-Fi away from account takeover:

  • No Secure → cookie travels over plain HTTP on the first request before HSTS kicks in. Sniffable on coffee-shop Wi-Fi.
  • No HttpOnly → any injected script (XSS, malicious npm dependency, hostile ad) can read the cookie via document.cookie and exfiltrate it.
  • No SameSite → cross-site request forgery (CSRF). An evil page can submit forms or trigger requests as your logged-in user.

Browsers have been tightening defaults: Chrome ships SameSite=Lax as the default now, but legacy code that explicitly sets cookies without flags still leaks.

How to fix it

Set every session/auth cookie with all three flags.

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

Express / Node.js:

res.cookie("session", token, {
  httpOnly: true,
  secure: true,
  sameSite: "lax",
  maxAge: 86400 * 1000,
  path: "/",
});

Next.js (App Router with cookies()):

import { cookies } from "next/headers";

cookies().set({
  name: "session",
  value: token,
  httpOnly: true,
  secure: true,
  sameSite: "lax",
  path: "/",
  maxAge: 86400,
});

Bun.serve():

return new Response("ok", {
  headers: {
    "Set-Cookie": `session=${token}; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=86400`,
  },
});

Django:

# settings.py
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "Lax"
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
CSRF_COOKIE_SAMESITE = "Lax"

Rails:

# config/initializers/session_store.rb
Rails.application.config.session_store :cookie_store,
  key: "_app_session",
  secure: Rails.env.production?,
  httponly: true,
  same_site: :lax

PHP / WordPress: in wp-config.php or before any output:

ini_set('session.cookie_secure', '1');
ini_set('session.cookie_httponly', '1');
ini_set('session.cookie_samesite', 'Lax');

Which SameSite value?

  • Strict — cookie never sent on cross-site requests. Maximum protection, but breaks the case where users follow a link to your site and arrive logged out. Good for high-stakes flows (admin, banking).
  • Lax (default) — cookie sent on top-level navigations (clicking a link) but not on cross-site POSTs, iframes, or image requests. The right default for most sites.
  • None — cookie sent on all cross-site requests. Requires Secure. Use only when you genuinely need third-party context (embedded widgets, identity providers).

Cookie name prefixes add a second layer of enforcement:

  • __Secure- — browser refuses to set this cookie unless Secure is also set.
  • __Host- — browser refuses unless Secure, Path=/, and no Domain attribute.
Set-Cookie: __Host-session=abc123; Path=/; Secure; HttpOnly; SameSite=Lax

Pair this rule with HTTPS, enable HSTS, and the referrer policy for a complete transport-security baseline.

Frequently asked questions

Will HttpOnly break my client-side code?

Only if you read the cookie from JavaScript. For session/auth cookies, you shouldn’t — the cookie should be opaque to the client and only used by the server. If you’re storing data the JS needs to read, that’s a different category of cookie (preferences, UI state) and HttpOnly doesn’t apply.

Consent and analytics cookies often genuinely need to be JavaScript-readable. Set Secure and SameSite=Lax on them, but skip HttpOnly. The MetricSpot check tolerates this — it only warns when a session cookie is missing the flags.

Does this affect GDPR compliance directly?

Not the consent/legal-basis side, but the security side. GDPR Article 32 requires “appropriate technical measures” — missing cookie flags are routinely cited in supervisory authority decisions as a security failure. Pair with a cookie consent banner and privacy policy for the legal layer.

Sources

Last updated 2026-05-11