Cookies not working after deploying (Render + Netlify) — SameSite vs Secure explained

Issue

I ran into a cookie/auth issue when deploying my app.

  • Local setup
    • NestJS backend (HTTP)
    • Next.js frontend (HTTP)
    • Login worked correctly
  • Deployed setup
    • Backend deployed to Render (HTTPS)
    • Frontend deployed to Netlify (HTTPS, Netlify subdomain)
    • Login stopped working

Root cause

After debugging, I realized this was a cookie + SameSite issue, not a NestJS or CORS bug.

  • *.netlify.app and *.onrender.com are different sites
  • Because they are cross-site, cookies with SameSite=Lax are not sent on fetch / XHR requests
  • As a result, the auth cookie was never included after login

This worked locally because:

  • Both frontend and backend were running on localhost
  • SameSite rules treated them as same-site

Important findings

  1. SameSite ≠ Same Origin “SameSite” is based on the registrable domain (eTLD+1), not the full origin. Examples:
  • a.letitcode.devb.letitcode.dev:white_check_mark: same-site
  • a.onrender.comb.onrender.com:white_check_mark: same-site
  • a.netlify.appb.onrender.com:cross_mark: cross-site
  • a.co.ukb.co.uk:cross_mark: cross-site (co.uk is a public suffix)
  1. HTTP frontend cannot use cookies from an HTTPS backend Even if they are same-site:
  • Secure cookies are never sent from HTTP pages
  • If the backend is HTTPS, the frontend must also be HTTPS
  1. Cross-site cookies require strict settings To allow cookies across different sites:
  • SameSite must be ‘none’
  • Secure must be true (mandatory)
  • Frontend and backend must both be HTTPS

Solution

I updated the backend cookie config to explicitly support cross-site usage:

import { CookieOptions } from 'express';

const cookieOptions: CookieOptions = {
  httpOnly: true,
  secure: true,          // Required when SameSite=None
  sameSite: 'none',      // Required for cross-site (Netlify ↔ Render)
  expires: new Date(Date.now() + 24 * 60 * 60 * 1000),
};

After this change (and with correct CORS + credentials: ‘include’ on the frontend), login worked as expected.


Summary

  • Localhost hides many cookie issues
  • Render + Netlify is a cross-site setup
  • SameSite=Lax is not enough for cross-site fetch
  • SameSite=None always requires Secure=true
  • HTTPS frontend is mandatory when using Secure cookies

Hopefully this helps anyone hitting the same “works locally, breaks in prod” problem.

Secure cookies can appear to work from any local frontend only when the backend runs on a loopback address (e.g. localhost), because browsers treat loopback request targets as secure contexts even over HTTP for local development.

Common loopback addresses

Address Type
localhost Hostname
127.0.0.1 IPv4 loopback
::1 IPv6 loopback