← All news
security5 min read

Cookie security flags that AI-generated session code leaves out

By Seaworthy · 25 June 2026

Laptop open on a wooden desk with a coffee cup nearby, representing a developer's working environment where browser session security decisions are made

Photo by NordWood Themes on Unsplash

Cookie security flags that AI-generated session code leaves out

If you have ever asked an AI assistant to set up session handling, you have probably seen something like this:

res.cookie('session', token, { httpOnly: true });

One flag. Sometimes zero. The Secure flag missing. SameSite not set. And because the code runs fine in local development, nobody notices until it matters.

This is not a complaint about AI tools. It is just the reality of how they generate code: they optimise for "this works" rather than "this is safe." Session cookies sit at the intersection of authentication and browser behaviour, and getting them wrong opens up attack surface that is completely avoidable.

Here is what each flag actually does and why you should not ship without it.


HttpOnly

This one does get included most of the time. HttpOnly tells the browser not to expose the cookie to JavaScript, which means document.cookie cannot read it. An XSS payload that exfiltrates your session token becomes much harder to write if the token is not reachable from the DOM.

If your AI-generated code does not include httpOnly: true, add it now. There is no legitimate reason to read a session token from client-side JavaScript.

Secure

Secure restricts the cookie to HTTPS connections only. Without it, the browser will send the session cookie over plain HTTP, which means anyone on the same network can read it in transit.

In development you are probably on localhost over HTTP, so the flag appears to do nothing and AI tools often skip it entirely. But when your code reaches production, which runs on HTTPS, the flag becomes load-bearing. The pattern I use is to set it conditionally:

const isProduction = process.env.NODE_ENV === 'production';

res.cookie('session', token, {
  httpOnly: true,
  secure: isProduction,
  sameSite: 'lax',
  path: '/',
  maxAge: 60 * 60 * 24 * 7, // 7 days in seconds
});

This way the flag is always present in production without breaking your local dev loop.

SameSite

SameSite is the one that AI tools get wrong most consistently. There are three values: strict, lax, and none.

strict means the cookie is never sent on cross-site requests, including when a user follows a link from an email or another site into your app. That sounds appealing until users start complaining that clicking a link logs them out immediately.

lax is the practical default for most apps. The cookie is sent on top-level navigation (following a link) but not on cross-site subresource requests. This blocks the most common CSRF vectors while keeping the user experience intact.

none must be paired with Secure: true and is only appropriate when your API is intentionally serving cross-origin requests, for example an embedded widget or a third-party checkout flow.

Modern browsers default to lax when SameSite is not set, but you should not rely on that. Browser behaviour varies across versions and enterprise configurations. Set it explicitly.

Putting it together in Next.js

If you are using Next.js App Router with route handlers, the pattern looks like this:

import { cookies } from 'next/headers';

export async function POST(req: Request) {
  const token = await createSessionToken();

  const cookieStore = await cookies();
  cookieStore.set('session', token, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    sameSite: 'lax',
    path: '/',
    maxAge: 60 * 60 * 24 * 7,
  });

  return Response.json({ ok: true });
}

Note maxAge rather than expires. maxAge is relative (seconds from now), while expires is absolute and requires you to construct a Date object. maxAge is simpler and less error-prone.

Also notice path: '/'. Without it, the cookie scope defaults to the path of the request that set it. If you set a session cookie in /api/auth/login, then a request to /dashboard will not send the cookie at all. Always set path: '/' for session cookies unless you have a specific reason to scope them more narrowly.

What about the __Host- prefix?

If you want to go further, prefix your cookie name with __Host-. This forces the browser to require Secure: true, reject any Domain attribute, and restrict the path to /. It is a browser-enforced hardening layer on top of the flags above:

cookieStore.set('__Host-session', token, {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
  path: '/',
});

The downside is that __Host- cookies do not work at all over HTTP, which means your local dev environment needs to run on HTTPS or you need a separate cookie name for development.

The actual problem with AI-generated session code

AI tools are trained on a massive corpus of code, most of which was written before SameSite became widely supported and before Secure was considered non-negotiable. The patterns they reproduce reflect that history. You will get code that runs, but you will not get code that has been audited.

The fix is mechanical: treat cookie configuration as a checklist, not something to leave to defaults. HttpOnly, Secure, SameSite, and path are the four you should verify every time.

Seaworthy's configuration checks include detection of session cookies missing one or more of these flags, flagging them as issues in the static analysis report.

This article was generated by AI and summarises publicly available sources.

From the makers of Seaworthy

Seaworthy scans your repo for the issues covered in articles like this one.

Security gaps, exposed secrets, and misconfigurations — caught before you deploy. Free to run, no account needed.

$ npx seaworthycode