NextAuth Works Locally but Fails in Production

In Brief
When NextAuth works locally but fails on Vercel, configuration is usually the first place to look. Check the exact failed flow, canonical auth URLs, NEXTAUTH_URL, secrets, cookies, provider callback settings, middleware, HTTPS assumptions, and preview domains before assuming the library behaves differently in production.
Authentication bugs are at their most irritating when they only happen in production.
NextAuth works locally. Sign‑in succeeds. Sessions are readable. Protected routes behave. Then the deployed site loops, drops the session, sends users to the wrong domain, fails callback validation, or behaves differently between preview and production.
That usually means the core auth code is not the first place to look. Production auth failures tend to live in URLs, cookies, secrets, provider configuration, middleware, deployment domains, and environment variables.
Start with the Exact Failed Flow
Do not debug "auth is broken". Debug one flow.
Capture:
- provider used
- sign‑in URL
- callback URL
- final redirect URL
- browser console errors
- network responses
- cookies set or missing
- server logs
- preview or production environment
- user role or account state
The difference between "callback rejected", "session not persisted", "middleware loop", and "provider refused redirect URI" matters. They can all look like users being sent back to sign in.
If the whole site broke after a release, use a broader production triage checklist first. If only auth is failing, narrow the scope quickly.
Check Canonical Auth URLs
NextAuth configuration depends heavily on the public URL of the app.
Check:
NEXTAUTH_URLAUTH_URLif using newer Auth.js naming- provider redirect URIs
- production domain
- preview domain
- custom base path
- trailing slash behaviour
- HTTP versus HTTPS
The NextAuth documentation says NEXTAUTH_URL should be set to the canonical URL in production, and also documents NEXTAUTH_SECRET. The newer Auth.js deployment docs are useful if the project has moved to the Auth.js naming model.
Relevant references:
The important practical point is this: local callback URLs and production callback URLs are different. OAuth providers usually need the production callback URL registered explicitly. Preview deployments may need their own strategy or may need auth disabled for public review environments.
Verify Secrets and Environment Scope
Missing or inconsistent secrets cause production‑only failures.
Check:
- secret exists in production
- secret exists in preview if preview auth is expected
- secret did not change unintentionally
- provider client ID and secret match the provider app
- variables are scoped to the correct Vercel environment
- variables are not only present in
.env.local - server‑only variables are not expected in client code
Managing environment variables in Next.js covers the broader pattern. For auth, the risk is sharper because a different secret can invalidate or fail to decode sessions.
Never log secrets to debug this. Log whether expected variable names are present, not their values.
Inspect Cookies in the Browser
Auth bugs often become clear in the cookie panel.
Look for:
- session cookie set after callback
- cookie domain
SecureflagHttpOnlyflagSameSitebehaviour- cookie path
- different cookies on preview and production
- cookies set on the wrong subdomain
- old cookies surviving across deployments
Local development is forgiving. Production HTTPS, custom domains, preview domains, and cross‑site provider redirects are not.
If the callback succeeds but the next request looks unauthenticated, the cookie is a prime suspect. If the cookie is set on one domain and the app expects it on another, the session will appear to vanish.
Check Middleware and Route Protection
Middleware can turn a small auth problem into a loop.
Common mistakes:
- protecting the auth callback route
- protecting static assets
- redirecting already signed‑in users back to sign in
- reading a cookie name that changed
- using different auth config in middleware and API routes
- failing open locally and failing closed in production
- matching too many routes
Review route matchers carefully. A middleware rule that looks sensible for /account/:path* may become dangerous if it also catches /api/auth/:path*, preview routes, or provider callbacks.
The older article on using middleware in Next.js for route protection is useful context, but production auth debugging should always inspect the actual deployed matchers.
Check Provider‑Side Settings
OAuth providers are strict, and rightly so.
Check:
- redirect URI exactly matches production callback
- provider app is not still in development mode
- allowed domains include the production domain
- scopes have not changed
- client secret has not rotated
- user test account is permitted
- callback URL includes base path if the app uses one
The error might not be in your Next.js code. It may be the identity provider rejecting the deployed domain.
Check Session Strategy and Data Availability
If the app uses a database‑backed session, production needs the database and adapter to behave exactly as expected.
Check:
- database URL
- migrations
- adapter configuration
- network access
- connection limits
- user table state
- account linking data
- clock skew on token expiry
If the app uses JWT sessions, check secret stability, token size, cookie limits, and callback logic.
Production data can expose cases that local fixtures never covered: old users, duplicate emails, missing profile fields, or accounts linked before a provider changed its payload.
Wrapping Up
When NextAuth works locally but fails in production, the fix is usually not hidden inside a React component.
Start with the failed flow, then check canonical URLs, provider callback settings, secrets, cookie behaviour, middleware, and session storage. Production auth is a contract between your app, the deployment platform, the browser, and the identity provider. Any one of those can be slightly wrong.
The fastest debugging path is to follow the request from sign‑in to callback to cookie to protected route, and stop where the state disappears.
Key Takeaways
- Debug one auth flow at a time.
- Check production callback URLs and provider redirect settings first.
- Verify environment variables and secrets by scope, not only by name.
- Inspect cookies for domain, Secure, SameSite, and path issues.
- Make sure middleware does not protect auth callback routes or create loops.
- Check database or JWT session assumptions with production data.