NextAuth Session Null in Production: 7 Causes and Fixes
Login works locally but session is null on Vercel? Check NEXTAUTH_URL, secret, trustHost, cookies, callback URLs, and SessionProvider. Exact fixes for NextAuth and Auth.js.
NextAuth (Auth.js) returns a null session in production when the secret or URL is missing or wrong, when the cookie is not being set or sent because of domain or HTTPS settings, or when the OAuth callback URL does not match what the provider has registered.
Work through the causes in this order. They are sorted by how often they cause the problem.
1. Missing or Mismatched Secret
Production requires a secret to sign and encrypt the session. In NextAuth v4 it is NEXTAUTH_SECRET; in Auth.js v5 it is AUTH_SECRET. Without it you get a NO_SECRET error in production or sessions that cannot be decoded.
openssl rand -base64 32
Set the same value in every environment that handles sessions. Using a different secret on two deployments logs users out whenever they hit the other one.
2. Wrong Base URL
NextAuth v4 needs NEXTAUTH_URL set to your canonical production URL, such as https://app.example.com. Setting it to http://localhost:3000 in production is a classic copy-paste mistake and breaks callbacks and cookies. On Vercel, v4 can infer the URL from VERCEL_URL, but a custom domain should be set explicitly.
3. Behind a Proxy: Set trustHost
For Auth.js v5 on self-hosted setups, Docker, or any reverse proxy, the host header may not match what Auth.js expects, producing an UntrustedHost error. Set AUTH_TRUST_HOST=true or trustHost: true in your config, and make sure your proxy forwards X-Forwarded-Host and X-Forwarded-Proto. See also nginx 502 and proxy issues.
4. Cookie Not Set or Not Sent
On HTTPS, NextAuth uses __Secure- prefixed cookies, which browsers only accept over HTTPS. Check the Network tab: the login response should contain Set-Cookie, and the next request should send it.
| Problem | Fix |
|---|---|
Cookie domain doesn't match |
Set it to the exact host, or a parent domain for subdomains |
sameSite: 'strict' on OAuth return |
Use lax so the cookie survives the redirect from the provider |
| TLS ends at the proxy, app thinks HTTP | Forward X-Forwarded-Proto: https |
A cookie that is dropped also produces redirect loops in middleware.
5. OAuth Callback URL Mismatch
The provider (Google, GitHub) must list the exact callback, for example https://app.example.com/api/auth/callback/google. A staging URL registered but not the production one causes redirect_uri_mismatch or a silent return to the login page.
6. Session Read in the Wrong Place
// Client components need the provider
import { SessionProvider } from 'next-auth/react'
export function Providers({ children }: { children: React.ReactNode }) {
return <SessionProvider>{children}</SessionProvider>
}
Without SessionProvider, useSession does not behave as expected. On the server, use auth() (v5) or getServerSession(authOptions) (v4) and pass the same options object you used in the route handler. Pass a different config and the session reads as null.
7. Edge Runtime Limits
Middleware runs on the Edge runtime. If your adapter or callbacks use Node-only modules, the session check fails there. Use a split config with an edge-safe auth file for middleware. Related crash patterns are in Vercel Edge Runtime errors.
Quick Production Checklist
AUTH_SECRET/NEXTAUTH_SECRETset in production scope. See env vars undefined.NEXTAUTH_URLequals the real public URL.- Callback URL registered with the provider.
- Cookies visible in DevTools, with the right domain and
Secure. trustHostenabled behind a proxy.- Redeploy after changing any variable.
Let Relia Catch Login Breaks
Auth failures rarely throw: users just land back on the login screen. Relia watches users' sessions, notices when sign-ins start failing, and writes the incident up in plain words: what happened, why it thinks so (with a confidence level), and one thing to do next. On request it writes a fix plan with a "Copy for your AI assistant" button; Relia doesn't change code itself, and after you ship it checks the fix against live traffic.
FAQ
Why is getServerSession null only in production?
Usually a missing secret, a wrong NEXTAUTH_URL, or cookies not sent because of domain or HTTPS settings.
What does UntrustedHost mean in Auth.js?
Auth.js does not trust the incoming host header. Enable trustHost and forward the correct proxy headers.
Do I need to redeploy after changing NEXTAUTH_URL?
Yes. Environment changes apply only to new deployments.