Secure Cookies:HttpOnly, Secure and SameSite Explained
What the HttpOnly, Secure and SameSite cookie attributes do, which attacks each one reduces, sensible defaults for session cookies, and the cookie prefixes that add extra guarantees.

Table of Contents
Cookies often hold the keys to a user's session, so how they are set matters. Three attributes do most of the work: Secure (only send the cookie over HTTPS), HttpOnly (JavaScript cannot read it, which limits damage from cross-site scripting) and SameSite (controls whether the cookie is sent with requests from other sites, which reduces cross-site request forgery). For a typical session cookie, a sensible default is Secure; HttpOnly; SameSite=Lax, with a limited lifetime.
A session cookie, annotated
Set-Cookie: __Host-session=abc123; Path=/; Secure; HttpOnly; SameSite=Lax; Max-Age=7200
__Host-prefix: extra guarantees (explained below).Path=/: sent for the whole site.Secure: never sent over plain HTTP.HttpOnly: not readable bydocument.cookie.SameSite=Lax: not sent on most cross-site requests.Max-Age=7200: expires after two hours.
Secure
A cookie with Secure is only sent over HTTPS, so it cannot be captured on an unencrypted connection. With HTTPS everywhere (and HSTS), there is no reason to send session cookies any other way. See what is SSL/TLS and how does HTTPS work.
HttpOnly
HttpOnly stops page scripts from reading the cookie. If an attacker manages to inject JavaScript (cross-site scripting), they cannot simply copy the session cookie and use it elsewhere. It does not prevent XSS itself, which still needs output escaping and a Content Security Policy (see HTTP security headers explained).
Use HttpOnly for every cookie that scripts do not need to read: sessions, authentication, CSRF secrets.
SameSite
SameSite controls whether a cookie is sent when a request comes from another site:
| Value | Behaviour | Typical use |
|---|---|---|
Strict |
Never sent on cross-site requests, including following a link from another site | Highly sensitive actions; may log users out when arriving from links |
Lax |
Sent on top-level navigations (clicking a link) but not on cross-site form posts, images or iframes | Default choice for session cookies |
None |
Sent on all requests; must also be Secure |
Embedded content and some third-party integrations |
Modern browsers treat cookies without a SameSite attribute as Lax by default, but setting it explicitly avoids surprises.
SameSite reduces cross-site request forgery (CSRF), but keep CSRF tokens for state-changing forms as well; frameworks such as Laravel and WordPress provide them.
Cookie prefixes
__Secure-: the browser only accepts the cookie if it hasSecureand was set over HTTPS.__Host-: additionally requiresPath=/and noDomainattribute, so the cookie cannot be set or overwritten by subdomains.
__Host- is a strong choice for session cookies when subdomains should not share the session.
Example: how SameSite stops a cross-site request
Suppose a shop lets logged-in customers change their delivery address with a form that posts to https://shop.example/account/address.
An attacker's page on another site contains a hidden form that posts a new address to that URL and submits itself automatically when a logged-in customer visits:
- Without
SameSite(or withSameSite=None), the browser attaches the customer's session cookie to the cross-site POST, and the shop accepts the change as if the customer made it. - With
SameSite=Lax, the browser does not send the session cookie on a cross-site POST, so the shop sees an unauthenticated request and rejects it. - A CSRF token in the real form gives a second layer: the attacker's page cannot read the token, so even if cookies were sent, the request would fail validation.
Checking your cookies
- Open the site, log in, and open the browser's developer tools.
- Go to Application (Chrome/Edge) or Storage (Firefox) → Cookies.
- For each cookie, check the Secure, HttpOnly and SameSite columns, the Domain, Path and Expires.
- Session and authentication cookies should be Secure, HttpOnly and Lax or Strict, with a narrow domain.
Cookies that scripts need to read (for example a user's theme preference) cannot be HttpOnly; make sure they contain nothing sensitive.
Other good practices
- Short lifetimes for session cookies, with re-authentication for sensitive actions.
- Regenerate the session ID after login, to prevent session fixation.
- Do not store sensitive data in cookies; store an identifier and keep data on the server.
- Limit the
Domainattribute: setting it to the parent domain shares the cookie with every subdomain. - Invalidate sessions on logout on the server, not just by deleting the cookie.
In common frameworks
- Laravel: session cookie settings live in
config/session.php(secure,http_only,same_site); setSESSION_SECURE_COOKIE=truein production. - WordPress: login cookies are
HttpOnly, andSecurewhen the site uses HTTPS (FORCE_SSL_ADMINhelps for the admin).
Frequently Asked Questions
Do these attributes stop all session hijacking?
They remove the most common ways cookies are stolen or misused, but not every risk, such as malware on the user's device. Combine them with HTTPS, CSP, short sessions and 2FA.
Why did my embedded widget stop working?
Cross-site cookies need SameSite=None; Secure, and browsers increasingly restrict third-party cookies altogether. Prefer designs that do not depend on them.
Are cookies covered by privacy laws?
Often, yes. Many jurisdictions require consent for non-essential cookies such as analytics and advertising. Strictly necessary cookies, like sessions, are usually treated differently.
Related reading
See API security basics and the Laravel security checklist, and for the bigger picture, website security basics. For secure applications built for you, see ServerNeed custom web development.
Sources
Last updated 7 October 2026



