Skip to content
← All articles

Cookie Security: HttpOnly, Secure, SameSite, __Host-

Cookie Security: HttpOnly, Secure, SameSite, __Host-

Cookies are the core state mechanism of HTTP. The flags HttpOnly, Secure, SameSite and the __Host-/__Secure- prefixes decide whether XSS can steal a session, whether CSRF works, and whether a cookie can ride on HTTP. This post is a full walkthrough of the RFC 6265bis attributes with PHP, Express, and nginx examples.

HttpOnly

HttpOnly prevents JavaScript from reading the cookie through document.cookie. Mandatory on session cookies — without it any XSS steals the token. Reference: RFC 6265 §4.1.2.6.

Set-Cookie: session=abc123; HttpOnly

Secure

Secure restricts the cookie to SSL/TLS проверку. Without it, the browser will send the cookie over HTTP — a gift to any on-path attacker. Mandatory on every HTTPS cookie.

Set-Cookie: session=abc123; HttpOnly; Secure

SameSite

SameSite mitigates CSRF. Three values:

Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict

Lax fits standard web-app sessions, Strict is for critical operations (banking), None is only for cookies used in third-party iframes (rare).

__Host- and __Secure- prefixes

Prefixes are browser-enforced markers:

Set-Cookie: __Host-session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/

Max-Age vs Expires

Max-Age is a delta in seconds, UTC-safe. Expires is an absolute GMT date. Prefer Max-Age:

Set-Cookie: session=abc; Max-Age=3600     ← 1 hour
Set-Cookie: session=abc; Max-Age=0         ← delete

PHP

session_set_cookie_params([
  'lifetime' => 0,
  'path'     => '/',
  'secure'   => true,
  'httponly' => true,
  'samesite' => 'Strict',
]);
session_start();

setcookie('csrf_token', $token, [
  'expires'  => time() + 3600,
  'path'     => '/',
  'secure'   => true,
  'httponly' => true,
  'samesite' => 'Strict',
]);

Express / NestJS

app.use(session({
  secret: process.env.SESSION_SECRET,
  cookie: {
    httpOnly: true,
    secure: true,
    sameSite: 'strict',
    maxAge: 3600_000,
  },
}));

res.cookie('token', jwt, {
  httpOnly: true,
  secure: true,
  sameSite: 'strict',
  maxAge: 3600_000,
  path: '/',
});

Session fixation and regenerate_id

Always call session_regenerate_id(true) (PHP) or equivalent after login — otherwise an attacker can pre-plant a session ID.

if (loginSuccess($user, $pass)) {
    session_regenerate_id(true);
    $_SESSION['user_id'] = $user['id'];
}

Analysing cookies

curl -I https://example.com | grep -i set-cookie

The enterno.io Cookie Security Analyzer inspects every cookie, flags missing attributes and trackers, and assigns a grade.

FAQ

Is SameSite=Lax safe? For most apps, yes. For banking-grade operations use Strict plus a CSRF token.

Why doesn't my cookie show up in cross-domain fetch? Requires credentials: 'include', CORS with Allow-Credentials: true, and SameSite=None; Secure.

Does __Host- break subdomains? Yes — scoped to the exact host. app.example.com and API документацию.example.com need separate cookies.

JWT in a cookie? Yes — safer than localStorage (HttpOnly defeats XSS). Pair with CSRF token or SameSite=Strict.

Conclusion

Canonical session cookie: HttpOnly; Secure; SameSite=Strict; Path=/; __Host- prefix. Audit with the Cookie Analyzer and the Security Scanner. Related: Clickjacking, XSS protection.

Check your website right now

Check now →
More articles: SEC
SEC
API Rate Limiting: Token Bucket, 429, Retry-After
15.04.2026 · 4 views
SEC
CSP (Content Security Policy): Setup Guide
15.04.2026 · 4 views
SEC
WAF (Web Application Firewall): A Practical Guide
15.04.2026 · 4 views
SEC
Clickjacking Prevention: X-Frame-Options vs frame-ancestors
15.04.2026 · 6 views