Session cookie flags
From express-session’s documentation: “By default, the Secure attribute is not
set.” The package cannot know whether you are serving over HTTPS, so it declines to
guess — which means the flag is yours to set, and nothing warns if you never do.
app.set("trust proxy", 1); // see the note below — this is not optional
app.use( session({ // ... cookie: { secure: true, // HTTPS only httpOnly: true, // not readable from JavaScript sameSite: "lax", // sent on top-level navigation, not cross-site POSTs maxAge: 1000 * 60 * 60 * 8, }, }));curl -sI https://example.com/login | grep -i set-cookieLook for Secure; HttpOnly; SameSite=Lax on the real response. Reading the config
proves nothing here, because the most common failure produces a config that says
secure: true and a response with no cookie at all.
Why secure: true alone can log everyone out
Section titled “Why secure: true alone can log everyone out”This is the trap, and express-session’s docs call it out directly:
If
secureis set, and you access your site over HTTP, the cookie will not be set. If you have your node.js behind a proxy and are usingsecure: true, you need to set “trust proxy” in express.
Behind a TLS-terminating proxy, Express receives plain HTTP. With secure: true and
trust proxy unset, express-session decides the connection is insecure and declines
to send the cookie at all. There is no error. Login appears to succeed and the user
is immediately anonymous again.
app.set("trust proxy", 1) tells Express to believe X-Forwarded-Proto from the
first hop. It is the same one-line-either-side problem as Django’s
SECURE_PROXY_SSL_HEADER, and it comes with the same caveat — the proxy must strip
that header from client requests, or anyone can assert it. That is on
trust proxy.
secure: true breaks local development over http://localhost for exactly the reason
above. Gate it on the environment rather than hardcoding it:
const isProd = process.env.NODE_ENV === "production";cookie: { secure: isProd, httpOnly: true, sameSite: "lax" }sameSite: "strict" withholds the cookie even on a top-level navigation from another
site, so a user following a link from an email arrives logged out. lax is the right
default for almost every application.
Related
Section titled “Related”- trust proxy — required for
secure: truebehind a proxy - Session secret and store — what signs this cookie