Cookie set without Secure, or a session cookie without HttpOnly
Your home page sets a cookie that can travel over plain HTTP, or a session cookie that any script on the page can read.
- Severity
- Medium
- Who can exploit it
- Scripts on your page, or someone on the visitor’s network
- Found by
- PatchLabs Live posture: Set-Cookie headers on your home page
- Lab proof
- Not lab-proven: read from your home page’s response
What it is
Two flags decide who can get hold of a cookie. Secure: only sent over HTTPS, never over plain HTTP. HttpOnly: invisible to JavaScript, so an injected script can’t read it. PL-033 means a cookie set by your home page has no Secure, or a cookie whose name marks it as a session (such as session, sid, auth or token) has no HttpOnly. The report names the cookie and the missing flag.
For a session cookie, each missing flag is a separate way to lose the session: over the network, or to a script. SameSite isn’t part of this finding: browsers treat a cookie without it as Lax, which already keeps it out of most requests other websites trigger. Setting SameSite=Lax explicitly is still good practice.
Why AI builders produce it
Plain supabase-js keeps the session in localStorage, so many AI-built apps set no cookies at all and pass this check. Cookies appear when the app moves to server rendering or @supabase/ssr, or adds a server framework, and each library has its own defaults. Code written for localhost often leaves out Secure because it breaks plain-HTTP development.
What we see
A synthetic example for app.example.com, shortened. Not taken from any real site.
$ curl -sI https://app.example.com
HTTP/2 200
set-cookie: session=eyJ…; Path=/
# no Secure, no HttpOnly
Check it yourself
curl -sI https://your-app.com | grep -i '^set-cookie'
# every cookie should carry Secure; session cookies also HttpOnly
Cookies set after sign-in don’t show up on a signed-out request. In the browser, sign in, open DevTools → Application → Cookies and read the Secure and HttpOnly columns for every cookie on your domain.
The fix
Set Secure on every cookie, and HttpOnly on anything that identifies a user. Add SameSite=Lax while you’re there, so you don’t depend on the browser’s default; Strict also drops the cookie when someone follows a link to your site from elsewhere, which signs them out on arrival. Fix it where the cookie is created:
@supabase/ssr
@supabase/ssr stores the session in cookies so the server can read it. Its browser client reads those cookies too, so they can’t be HttpOnly; set secure explicitly (and sameSite while you’re there), and rely on a Content-Security-Policy (PL-030) to keep scripts off the page.
import { createServerClient } from '@supabase/ssr'
const supabase = createServerClient(url, anonKey, {
cookies: { getAll, setAll }, // your framework's cookie functions
cookieOptions: {
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
},
})
Next.js
import { cookies } from 'next/headers'
const store = await cookies()
store.set('session', token, {
httpOnly: true,
secure: true,
sameSite: 'lax',
path: '/',
})
Express
// a cookie you set yourself
res.cookie('session', token, { httpOnly: true, secure: true, sameSite: 'lax' })
// express-session: behind a proxy (Vercel, Render, Cloudflare), trust it so secure cookies are sent
app.set('trust proxy', 1)
app.use(session({ secret, cookie: { httpOnly: true, secure: true, sameSite: 'lax' } }))
Live posture findings come with the header or setting to change. PatchLabs never changes your hosting or your repo.
How PatchLabs finds it
[LOAD] https://app.example.com/ → 200 cookie session secure no · httponly no FLAG
Live posture reads the Set-Cookie headers on the responses of its one signed-out page load. It records each cookie’s name and flags, never its value, and never signs in. Every cookie needs Secure; HttpOnly is expected when the name looks like a session (it contains sess, auth, token, sid, jwt, login or remember). Several failing cookies count as one failed check in the grade. Cookies your app only sets after sign-in aren’t covered: check those in DevTools as above.
When the pattern is intended
A cookie that JavaScript must read, such as a theme or language preference, or the @supabase/ssr session cookie, can’t be HttpOnly. That is a deliberate trade, and the report says which flag is missing so you can judge it. Secure has no such trade-off on a live site.
Why it’s rated medium
Medium: a missing flag only matters alongside a second problem, such as a plain-HTTP connection or an injected script. On a session cookie, that second problem is easy to find. 15 points in the Live posture grade, counted once however many cookies fail.
Related findings
Any website can call a signed-in function
The endpoint tells browsers that any website may send your users' cookies to it and read the answer.
PL-030No Content-Security-Policy
Your page sends no Content-Security-Policy, so any script that gets onto it can run and send data anywhere.
PL-027Site doesn’t force HTTPS
The plain http:// address of your site serves the page instead of redirecting to https://.
Guides
Free check
Does your site have PL-033?
Type your site’s address. The free headers check loads your public home page once, like any visitor, and grades HTTPS, the certificate, security headers, cookie flags and mixed content. No sign-up, and the result isn’t stored.
Last reviewed 10 Nov 2026. Finding IDs are stable: link to them freely. Spotted a mistake? Tell us.