Set-Cookie header ignored — cookie not stored after login
appears when:When the frontend and API are on different registrable domains and the cookie is sent with SameSite=Lax (the default) or Secure=false
Cookies not set cross-domain in production
Modern browsers silently drop cross-site cookies that are not SameSite=None and Secure. The server thinks the cookie was set. The browser never stored it.
Set-Cookie: ...; SameSite=None; Secure on the server response, credentials: "include" on every client fetch, and Access-Control-Allow-Credentials: true with an explicit (non-wildcard) origin on the CORS response. Missing any of the three and the browser drops the cookie silently.Quick fix for cookies not set cross-domain
01// app/api/auth/login/route.ts — set cross-site cookie correctly02import { cookies } from "next/headers";03 04export async function POST(req: Request) {05 const { email, password } = await req.json();06 const session = await authenticate(email, password);07 08 const store = await cookies();09 store.set("session", session.token, {10 httpOnly: true,11 secure: true, // required with SameSite=None12 sameSite: "none", // allow cross-site sends13 path: "/",14 domain: ".example.com", // share across app.example.com + api.example.com15 maxAge: 60 * 60 * 24 * 7,16 });17 18 return Response.json({ ok: true });19}20 21// Client:22await fetch("https://api.example.com/auth/login", {23 method: "POST",24 credentials: "include", // required to send/receive cookies cross-site25 headers: { "Content-Type": "application/json" },26 body: JSON.stringify({ email, password }),27});Deeper fixes when the quick fix fails
01 · Share a cookie across subdomains of the same registrable domain
01// Best option when you own both apex and subdomains02store.set("session", token, {03 httpOnly: true,04 secure: true,05 sameSite: "lax", // same-site — Lax is enough06 domain: ".example.com", // shared across app.example.com, api.example.com07 path: "/",08});09 10// Client fetch does not need credentials: include for same-site11// but including it is harmless.02 · CORS middleware with credentials
01// middleware.ts — echo origin explicitly, never *02import { NextResponse, type NextRequest } from "next/server";03 04const ALLOWED = new Set([05 "https://app.example.com",06 "https://staging.example.com",07]);08 09export function middleware(req: NextRequest) {10 const origin = req.headers.get("origin") ?? "";11 const res = NextResponse.next();12 13 if (ALLOWED.has(origin)) {14 res.headers.set("Access-Control-Allow-Origin", origin);15 res.headers.set("Access-Control-Allow-Credentials", "true");16 res.headers.set("Vary", "Origin");17 }18 19 if (req.method === "OPTIONS") {20 res.headers.set("Access-Control-Allow-Methods", "GET, POST, OPTIONS");21 res.headers.set("Access-Control-Allow-Headers", "content-type");22 return new NextResponse(null, { status: 204, headers: res.headers });23 }24 25 return res;26}27 28export const config = { matcher: "/api/:path*" };03 · Playwright regression test
01// tests/cross-site-auth.spec.ts02import { test, expect } from "@playwright/test";03 04test("cookie persists across cross-site pages", async ({ page, context }) => {05 await page.goto("https://app.example.com/login");06 await page.getByLabel("Email").fill("test@example.com");07 await page.getByLabel("Password").fill("test");08 await page.getByRole("button", { name: "Sign in" }).click();09 10 // Cookie should exist on the API domain11 const cookies = await context.cookies("https://api.example.com");12 const session = cookies.find((c) => c.name === "session");13 expect(session).toBeDefined();14 expect(session?.sameSite).toBe("None");15 expect(session?.secure).toBe(true);16});Why AI-built apps hit cookies not set cross-domain
The cookie spec evolved aggressively between 2020 and 2024. Chrome 80 (February 2020) flipped the default SameSite attribute from None to Lax. Before that change, any cookie set without an explicit SameSite was sent on every request, which created cross-site request forgery risks. After the change, cookies without an explicit attribute only go with same-site top-level navigations. Any cross-site fetch — including a login POST from app.example.com to api.example.com if those are separate registrable domains — drops the cookie.
AI-generated code trips on this because the scaffold was usually trained on examples where everything runs on localhost, which is same-origin. The scaffold sets sameSite: "lax" or omits the attribute entirely. In dev, the login works. In production on Vercel with a separate API domain, the server responds 200 OK with Set-Cookiein the header — but Chrome reads the header, evaluates the SameSite policy, and silently drops it. The app shows "logged in" briefly while the response is in memory, then every subsequent request is anon.
The second trap is the Secure attribute. Browsers refuse SameSite=None without Secure=true — the combination is treated as invalid and the cookie is dropped. Secure requires HTTPS. On Vercel preview deployments and production, everything is HTTPS, so this is fine. Some dev setups use HTTP for the API, which silently breaks the cookie even though the code looks correct.
The third trap is CORS. Setting the cookie attributes right is necessary but not sufficient. The browser also needs explicit permission to include credentials on the request and to accept credentials on the response. That means credentials: "include" on the fetch, Access-Control-Allow-Credentials: true on the server, and a specific origin in Access-Control-Allow-Origin — the wildcard * is rejected when credentials are involved.
cookies not set cross-domain by AI builder
How often each AI builder ships this error and the pattern that produces it.
| Builder | Frequency | Pattern |
|---|---|---|
| Lovable | Every multi-domain deploy | Default cookie attrs, no CORS credentials setup |
| Bolt.new | Common | Access-Control-Allow-Origin: * with credentials — browser rejects |
| Cursor | Common | sameSite: 'lax' hard-coded in template, never adjusted for cross-site |
| Base44 | Sometimes | Sets Secure: false in dev and forgets to toggle it for prod |
| Replit Agent | Rare | Uses localStorage instead of cookies, opens XSS exfil surface |
Related errors we fix
Stop cookies not set cross-domain recurring in AI-built apps
- →Prefer subdomains of one registrable domain (app.example.com + api.example.com) so SameSite=Lax works.
- →If genuinely cross-site, set SameSite=None AND Secure — browsers require both together.
- →Never use Access-Control-Allow-Origin: * with credentials — echo the specific origin from an allowlist.
- →Always pass credentials: 'include' on cross-site fetches — missing it drops cookies silently.
- →Test the full login flow in a Playwright suite that checks the cookie lands on the API domain.
Still stuck with cookies not set cross-domain?
cookies not set cross-domain questions
Why do cookies work on localhost but break when deployed?+
What is the difference between cross-site and cross-origin?+
Why do I need both SameSite=None and Secure?+
Do I still need CORS credentials settings?+
How long does a cross-domain auth audit take?+
Ship the fix. Keep the fix.
Emergency Triage restores service in 48 hours. Break the Fix Loop rebuilds CI so this error cannot ship again.
Hyder Shah leads Afterbuild Labs, shipping production rescues for apps built in Lovable, Bolt.new, Cursor, Replit, v0, and Base44. our rescue methodology.