How do I fix a cookie that is missing the Secure flag?

When a response sets a session cookie without the Secure attribute, the browser will send that cookie over plain HTTP. Finding a cookie missing the Secure flag is common after a stack upgrade, a proxy change, or a new cookie added by a third-party library. This page explains what Secure actually does and shows the exact directive to add it in Nginx, Apache, Express, and Next.js.

Why this matters

The Secure attribute instructs the browser to send the cookie only when the request uses the https: scheme, with localhost treated as an exception (MDN Set-Cookie). Without it, any request to the same host over http — an old bookmark, an outdated asset reference, or a link an attacker injects on a network the victim trusts — carries the session token in the clear. OWASP's Session Management Cheat Sheet calls the Secure attribute mandatory to prevent disclosure of the session ID through man-in-the-middle attacks, and it is explicit that forcing HTTPS on the server does not help if the cookie itself is not marked Secure: an attacker can still trick the browser into submitting the cookie over an unencrypted connection. MDN also notes that insecure (http:) sites cannot set a Secure cookie at all, so this flag only becomes effective once the whole site is reached over HTTPS. Secure is not the same as HttpOnly or SameSite: it controls transport, not script access or cross-site sending, so set all three on session cookies.

How to fix it

Set Secure on every cookie that carries a session, token, or credential, and set it where the cookie is created. The snippets below are drawn from each vendor's own documentation. Nginx's proxy_cookie_flags adds flags to cookies returned by an upstream proxy; the secure, httponly, samesite=strict, samesite=lax, and samesite=none parameters add the corresponding flags. Express and Next.js both accept a secure boolean when setting a cookie.

location / {
    proxy_pass http://app:3000;
    proxy_cookie_flags ~ secure httponly samesite=lax;
}
res.cookie('session', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax'
});
'use server'
import { cookies } from 'next/headers'

export async function setSession(token) {
  const cookieStore = await cookies()
  cookieStore.set('session', token, {
    httpOnly: true,
    secure: true,
    sameSite: 'lax',
    path: '/'
  })
}

Apache's mod_headers has no dedicated cookie-flag directive, so the documented edit action is used to search-and-replace the Set-Cookie value. Cloudflare Workers lets you append a full Set-Cookie response header, and its documented example already includes the HttpOnly flag.

Header edit Set-Cookie ^(.*)$ "$1; Secure"
const headers = new Headers();
headers.append("Set-Cookie", "session=" + token + "; Path=/; HttpOnly; Secure; SameSite=Lax");
return new Response(body, { headers });

How to verify the fix

Request the endpoint that sets the cookie and read the raw response header. The Secure attribute must appear on the Set-Cookie line. In a browser, open DevTools, go to Application, then Cookies, and confirm the Secure column is checked for the session cookie.

curl -sI https://example.com/login | grep -i "set-cookie"

Automated check

App Readiness Auditor requests your site, parses every Set-Cookie header it receives, and reports any cookie without the Secure attribute as "Cookie missing Secure flag: <name>" at Medium severity. The report includes the cookie name so you can trace the code that set it. The scan only sees cookies set on responses it can reach without authenticating; cookies issued only after a successful login are marked as not fully checked rather than assumed safe.

Scan your app free →

Related issues