Three flags decide whether a session can be stolen.
Enter a URL and we will list the cookies your site sets and check each one for Secure, HttpOnly and SameSite. Session cookies are the credential your users never see, and these three attributes are most of their protection.
What this checks, and what a good answer looks like
Secure
Without it, the browser will send the cookie over plain http as well as https. That sounds theoretical until you remember how it actually happens: a user types your domain without a scheme, or clicks an old http link, the browser makes one unencrypted request before your redirect fires, and the session cookie rides along in the clear on whatever network they are using. The redirect does not save you, because the cookie was already sent. Any cookie that identifies a user needs this.
HttpOnly
Without it, document.cookie can read the value, which means any script on the page can. That is the entire payoff of a cross-site scripting bug: the attacker does not need to do anything clever once they can read the session cookie, they just take it and become that user. With HttpOnly set, the same bug is still a bug, but the session survives it. This is the flag that decides how expensive your next XSS is.
SameSite
Whether the cookie is attached to requests that come from other sites. With no value set, a form on someone else's page can post to your endpoint and the browser will helpfully include the session, which is cross-site request forgery. Lax is the sensible default and is what modern browsers assume when the attribute is absent, but assuming a browser default is not the same as stating it, and older clients do not. Strict is safer and will log people out when they arrive from an external link. None is legitimate only for genuine cross-site use and requires Secure alongside it.
Which cookies we judge, and which we let pass
Not every cookie needs every flag. An analytics or preference cookie holding no identity is not worth locking down, and reporting it would train you to ignore the report. We weigh the ones whose names look like sessions and authentication, because those are the ones where a missing flag has a real consequence.
How to fix it
These are set where the cookie is created, not in a global config file, which is why they get missed: there is no single place to fix them and each library spells them differently. The examples below set all three at once.
Set-Cookie: session=abc123; Path=/; Max-Age=604800;
Secure;
HttpOnly;
SameSite=Lax
# Secure -> https only
# HttpOnly -> invisible to document.cookie
# SameSite -> not sent on cross-site requests
# Note: SameSite=None REQUIRES Secure, and browsers drop the cookie
# entirely if you set None without it. That silent drop is a common
# cause of "my login works locally and not in production".import { cookies } from "next/headers";
(await cookies()).set("session", token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
path: "/",
maxAge: 60 * 60 * 24 * 7,
});// Behind a proxy or load balancer, "secure" cookies are dropped
// unless Express is told to trust the forwarded protocol. Missing
// this is why the flag silently does nothing in production.
app.set("trust proxy", 1);
res.cookie("session", token, {
httpOnly: true,
secure: true,
sameSite: "lax",
maxAge: 1000 * 60 * 60 * 24 * 7,
});// Hono
import { setCookie } from "hono/cookie";
setCookie(c, "session", token, {
httpOnly: true,
secure: true,
sameSite: "Lax",
path: "/",
maxAge: 60 * 60 * 24 * 7,
});
// Fastify (@fastify/cookie)
reply.setCookie("session", token, {
httpOnly: true,
secure: true,
sameSite: "lax",
path: "/",
});We see the cookies your site sets on the one page we requested, before any login. The cookies that matter most are usually set at the moment someone signs in, on a response we never receive, so a clean result here is not proof that your session cookie is correct. Check the response to your own login request in devtools as well. And flags are only the transport half of the problem: they say nothing about how long the session lasts, whether it is invalidated on sign-out, or whether the token inside can be forged.