Web Security

How to Secure Cookies with HttpOnly, Secure, and SameSite

How to harden cookies with the HttpOnly, Secure and SameSite attributes, plus Path, Domain and the prefixes — and how to inspect them in browser DevTools.

StackOptic Research Team18 May 20269 min read
How to secure cookies with HttpOnly, Secure and SameSite

A cookie is just a small piece of data a site asks the browser to store and send back on future requests — but when that cookie is a session token, it is effectively the key to a logged-in account, which makes it one of the most valuable things an attacker can steal. The good news is that a handful of cookie attributes turn an exposed cookie into a hardened one without changing what it stores or how your app uses it. This guide explains the attributes that matter — HttpOnly, Secure, SameSite, Path, Domain, and the __Host-/__Secure- prefixes — what each protects against, and how to inspect any site's cookies in seconds.

It pairs naturally with how to protect your website from common attacks and with what is a Content Security Policy and how to set one, since cookie flags and CSP together blunt the impact of cross-site scripting.

Answer first: the attributes that matter

Cookies are set by the server using the Set-Cookie response header (or by JavaScript). Each cookie can carry attributes that instruct the browser how to handle it. For security, the ones that count are:

  • HttpOnly — hide the cookie from JavaScript.
  • Secure — only send it over HTTPS.
  • SameSite — control whether it is sent on cross-site requests.
  • Path and Domain — scope where it applies.
  • The __Host- / __Secure- name prefixes — let the browser enforce a safe configuration.

A well-secured session cookie typically looks like this in the Set-Cookie header:

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

Each part does a specific job. Let us take them in turn.

HttpOnly: keep cookies away from JavaScript

By default, any JavaScript running on a page can read that page's cookies via document.cookie. That is fine until an attacker manages to run their JavaScript on your page through cross-site scripting (XSS) — at which point they can read your users' cookies and send them off to a server they control. If the stolen cookie is a session token, the attacker can impersonate the victim.

The HttpOnly attribute closes this. A cookie marked HttpOnly is still sent automatically on requests to the server, but it is invisible to JavaScriptdocument.cookie cannot see it. So even if an XSS flaw lets an attacker run script, that script cannot read an HttpOnly session cookie and exfiltrate it.

To be clear about what HttpOnly is and is not: it does not prevent XSS — you still need input validation, output encoding and a Content Security Policy for that (see the CSP guide). What it does is remove one of the most damaging consequences of XSS, namely session theft. Every session and authentication cookie should be HttpOnly. The only cookies that should not be HttpOnly are the ones your client-side code genuinely needs to read, and those should never hold anything sensitive.

Secure: HTTPS only, never cleartext

The Secure attribute tells the browser to send the cookie only over an HTTPS connection and never over plain HTTP. Without it, a cookie can be transmitted on an http:// request — for example, the brief insecure request before a redirect to HTTPS, or any stray HTTP link — where a network attacker can capture it in cleartext.

Secure is the transport-layer complement to HttpOnly: where HttpOnly protects the cookie from script, Secure protects it on the wire. It works hand in hand with HSTS, which keeps the whole connection on HTTPS so there is no cleartext request in the first place — see what is HSTS and how to enable it. Belt and braces: HSTS keeps connections on HTTPS, and Secure guarantees the cookie is never sent over HTTP regardless. Every cookie of any sensitivity should be Secure.

SameSite: the CSRF defence

SameSite is the newest of the three core attributes and the one people understand least, so it is worth a careful explanation. It controls whether the browser includes the cookie on requests that originate from a different site.

Why does that matter? Because of cross-site request forgery (CSRF). In a CSRF attack, a malicious page (say, evil.example) causes the victim's browser to make a request to your site (yourbank.example) — for instance, submitting a hidden form that transfers money. Because the browser automatically attaches your cookies to requests to your domain, the forged request arrives carrying the victim's valid session cookie, and your server cannot easily tell it apart from a genuine action the user intended.

SameSite defeats this by limiting when the cookie travels on cross-site requests. It takes three values:

  • SameSite=Strict — the cookie is sent only on requests originating from your own site. Strongest protection, but it has a usability cost: if a user clicks a link to your site from an external page (an email, another website), the cookie is not sent on that first navigation, so they may appear logged out until they navigate within the site.
  • SameSite=Lax — the cookie is sent on same-site requests and on top-level navigations to your site (clicking a normal link), but not on cross-site sub-requests like a background form post or an embedded image. This is the balanced choice and is the modern browser default when no SameSite is specified, because it stops the dangerous cross-site cases while keeping inbound links working.
  • SameSite=None — the cookie is sent on all cross-site requests, restoring the old unrestricted behaviour. This is needed for legitimate cross-site scenarios (some third-party embeds, federated logins), but it must be paired with Secure — browsers reject SameSite=None without it.

For most session cookies, SameSite=Lax is the right default. Use Strict for the most sensitive actions where you can tolerate the inbound-link behaviour, and reserve None for genuine cross-site needs, always with Secure. Note that SameSite is a strong layer of CSRF defence but is best combined with traditional anti-CSRF tokens for critical actions, rather than relied on alone.

Path and Domain: scoping the cookie

Two more attributes control where a cookie is sent:

  • Path restricts the cookie to a URL path and its sub-paths. Path=/ (the whole site) is typical; a narrower path limits the cookie to part of the site.
  • Domain controls which hosts receive the cookie. If you omit Domain, the cookie is sent only to the exact host that set it (the most restrictive, usually safest). If you set Domain=example.com, the cookie is also sent to subdomains like app.example.com — broader, and only what you want if those subdomains genuinely need it. Broadening Domain unnecessarily widens exposure, so default to omitting it unless you have a reason.

The principle is least scope: a cookie should be sent to the narrowest set of paths and hosts that actually need it, and no wider.

The __Host- and __Secure- prefixes

There is one more layer that is easy to add and quietly valuable: cookie name prefixes. These are not separate attributes but special prefixes on the cookie's name that cause the browser to enforce a secure configuration and reject the cookie if it does not comply.

  • __Secure- — a cookie whose name starts with __Secure- must be set with the Secure flag, over HTTPS. The browser refuses it otherwise.
  • __Host- — stricter still: a cookie named with the __Host- prefix must be Secure, must have no Domain attribute (so it is locked to the exact host), and must have Path=/. The browser rejects it if any condition is unmet.

Why bother? Because they defend against cookie injection and fixation, where an attacker (often via a subdomain or a man-in-the-middle on a sibling host) tries to set or overwrite one of your cookies with a value they control. The __Host- prefix in particular ensures a cookie is bound to the exact host and cannot be shadowed by a cookie set for a parent domain or different path. For your most important cookies — session and authentication — naming them with __Host- is a cheap, strong hardening step.

A summary table

Attribute / featureWhat it doesPrimary protection
HttpOnlyHides cookie from JavaScriptMitigates session theft via XSS
SecureSends cookie only over HTTPSStops capture on cleartext HTTP
SameSite=StrictCookie only on same-site requestsStrong CSRF defence (stricter UX)
SameSite=Lax+ top-level navigations to your siteBalanced CSRF defence (good default)
SameSite=NoneCookie on all cross-site requestsEnables cross-site use (requires Secure)
PathScopes cookie to a URL pathLimits where the cookie is sent
Domain (omitted)Locks cookie to the exact hostAvoids over-broad subdomain exposure
__Secure- prefixForces Secure over HTTPSPrevents insecure overwrite
__Host- prefixForces Secure, no Domain, Path=/Strongest anti-injection binding

How to inspect a site's cookies

You can check exactly how any site configures its cookies, which is useful both for auditing your own and for understanding others.

1. Browser DevTools. This is the fastest way. Open developer tools and go to the Application tab (in Chrome and Edge) or the Storage tab (in Firefox), then expand Cookies and select the site's origin. You will see a table of every cookie with columns for HttpOnly, Secure, SameSite, Path, Domain, expiry and value. A quick scan tells you whether the session cookie is HttpOnly, Secure and has a sensible SameSite — and whether anything sensitive is missing a flag.

2. The Set-Cookie response header. Cookies are assigned by the server via Set-Cookie. You can read this header directly — in DevTools under Network → (the request) → Response Headers, or from a terminal on a response that sets a cookie. This shows the attributes the server is applying at the source. For the broader skill of reading response headers, see how to read a website's HTTP headers.

3. Header and security graders. Tools and broader audits — StackOptic among them — surface cookie and header configuration as part of a security picture, so you can see cookie hygiene alongside HTTPS, CSP and the rest. The Mozilla Developer Network (MDN) documents every cookie attribute authoritatively if you want the precise specification.

Common mistakes

  • Session cookies without HttpOnly, leaving them readable by any injected script — the most common and most damaging oversight.
  • Missing Secure, so a cookie can leak over a stray HTTP request.
  • No SameSite policy (or relying solely on the default) for actions that need CSRF protection, instead of consciously choosing Lax or Strict plus tokens.
  • SameSite=None without Secure, which browsers now reject outright — breaking the cookie.
  • An over-broad Domain that sends sensitive cookies to subdomains that do not need them.
  • Putting sensitive data in a non-HttpOnly cookie your front-end reads, exposing it to script.
  • Skipping the __Host- prefix on session cookies where it would add strong, free protection.

A quick cookie-hardening checklist

  • Mark every session/auth cookie HttpOnly and Secure.
  • Set a deliberate SameSite value — Lax as the default, Strict for sensitive flows.
  • If you use SameSite=None, always add Secure.
  • Omit Domain unless subdomains genuinely need the cookie; scope Path tightly.
  • Prefix critical cookies with __Host- to enforce a safe configuration.
  • Verify in DevTools → Application → Cookies that each flag is actually set.
  • Combine with a CSP and HSTS so the layers reinforce one another.

Go deeper

Want your cookie, header and HTTPS configuration checked at once? Analyse any URL with StackOptic — security, performance and SEO in one free report.

Frequently asked questions

What does the HttpOnly cookie attribute do?

HttpOnly tells the browser not to expose the cookie to client-side JavaScript — it is sent on HTTP requests but cannot be read via document.cookie. Its main value is mitigating session theft through cross-site scripting (XSS): even if an attacker manages to run script on your page, it cannot read an HttpOnly session cookie and exfiltrate it. It does not stop XSS itself, but it removes one of the most damaging things stolen script can do.

What is the difference between the Secure and HttpOnly flags?

They protect against different things. Secure controls transport: a cookie marked Secure is only ever sent over an HTTPS connection, never over plain HTTP, so it cannot be captured on a cleartext request. HttpOnly controls access: a cookie marked HttpOnly cannot be read by JavaScript in the page. A sensitive cookie, such as a session token, should carry both — Secure to protect it in transit and HttpOnly to protect it from script.

What does SameSite do and which value should I use?

SameSite controls whether a cookie is sent on requests originating from other sites, which is the core of CSRF defence. SameSite=Strict sends the cookie only on same-site requests (strongest, but can log users out when following inbound links). SameSite=Lax sends it on top-level navigations to your site (a balanced, common default). SameSite=None sends it on all cross-site requests and must be paired with Secure. Choose Lax unless you have a specific reason for Strict or None.

What are the __Host- and __Secure- cookie prefixes?

They are special name prefixes that make the browser enforce a secure configuration. A cookie named with the __Secure- prefix must be set with the Secure flag over HTTPS. A cookie with the __Host- prefix must additionally have no Domain attribute and a Path of /, locking it to the exact host that set it. Their purpose is to stop a weaker or attacker-set cookie from overwriting a secure one, hardening against cookie-injection and fixation attacks.

How do I check a website's cookie security settings?

Open your browser's developer tools and go to the Application tab in Chrome or Edge (Storage in Firefox), then expand Cookies and select the site. You will see each cookie with columns for HttpOnly, Secure, SameSite, Path, Domain and expiry, so you can verify sensitive cookies are configured correctly. From a terminal, a request that returns a Set-Cookie response header shows the attributes the server is assigning.

Analyse any website with StackOptic

Get the full technology stack, performance, security and SEO report in seconds — free.

Analyse a website

Related articles