Supabase Login Works Locally but Not on Vercel? 8 Fixes
Supabase authentication works locally but fails on Vercel? Check environment variables, redirect URLs, Site URL, Preview domains, cookies, and redeployment.

A Supabase login flow can work perfectly on localhost and then fail the moment the app is deployed to Vercel.
That happened to me while moving from local development into real Preview and Production deployments.
The confusing part is that several different problems can produce almost the same symptom:
- login appears to succeed but the user returns to the wrong page
- OAuth works locally but not on a Preview URL
- email confirmation links return to localhost
- password reset opens the wrong domain
- Supabase variables are missing in Preview
- the browser never keeps the expected session
- an old deployment is still using old configuration
The answer is not always "fix the login code."
I now debug the flow by separating the layers.
Quick answer: check these 8 things
If Supabase login works locally but not on Vercel, check:
- Confirm the correct Supabase environment variables exist in Vercel.
- Make sure the variables are available to the failing environment: Preview or Production.
- Redeploy after changing environment variables.
- Set the Supabase Site URL to the real production URL.
- Add the required Vercel Preview URLs to Supabase Redirect URLs.
- Make sure your code sends an allowed
redirectToURL. - Check whether cookies/session handling work correctly in the deployed environment.
- Test the exact new deployment, not an older Preview URL.
Most of the time, the failure is not one mysterious authentication bug.
It is a mismatch between the application URL, Vercel environment, and Supabase Auth configuration.
1. Check the Supabase variables in Vercel
Start with the simplest question.
Does the failing Vercel deployment actually have the Supabase configuration it needs?
A current public Next.js client may use:
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_...
The exact variable names depend on your code.
That is important.
If the code reads:
process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY
but Vercel stores:
NEXT_PUBLIC_SUPABASE_ANON_KEY
the application still sees the expected variable as missing.
Compare the code and Vercel settings directly.
2. Check Preview vs Production scope
A very common situation looks like this:
- localhost works
- Production works
- Vercel Preview fails
The code may be fine.
The required variables may be enabled only for Production.
Vercel separates its standard environments into:
- Development
- Preview
- Production
A pull request or non-production branch usually creates a Preview deployment.
If authentication fails only there, check the Preview variable scope before changing application logic.
I explain the environment difference in more detail in Vercel Preview vs Production Environment Variables Explained.
3. Redeploy after changing variables
Changing an environment variable in the Vercel dashboard does not retroactively rebuild an existing deployment.
After changing a variable:
- save it
- create a new deployment
- wait for the build to complete
- test the new URL
This is easy to miss when several Preview deployments exist.
You can fix the configuration correctly and still think it failed because you are testing an older deployment.
4. Set the Supabase Site URL correctly
In Supabase Auth URL Configuration, the Site URL is the default redirect destination when your code does not provide another allowed redirect.
For a production application, set it to the real production site.
For example:
https://www.example.com
Do not leave the production Site URL as:
http://localhost:3000
if real users are authenticating on the deployed site.
This setting matters for flows such as:
- email confirmation
- password reset
- magic-link login
- OAuth fallback redirects
Supabase's current documentation describes the Site URL as critical for email confirmations and password resets.
5. Add allowed Redirect URLs for Preview deployments
The Site URL alone is not enough when you intentionally want authentication to return to multiple environments.
Supabase also has a Redirect URLs allow list.
If your application passes a redirectTo value during authentication, that destination must match an allowed URL.
For local development, you might allow:
http://localhost:3000/**
For Vercel Preview deployments, Supabase supports wildcard patterns such as:
https://*-your-team-slug.vercel.app/**
Use the pattern that matches your actual Vercel Preview URLs.
For Production, Supabase recommends an exact production URL rather than an unnecessarily broad wildcard.
That gives you flexibility for previews without making the production rule vague.
6. Check the redirect URL your code is actually sending
The Supabase dashboard can be configured correctly while the application still sends the wrong destination.
For OAuth, OTP, password reset, or email confirmation flows, inspect the URL used by the code.
For example:
await supabase.auth.signInWithOAuth({
provider: "google",
options: {
redirectTo: callbackUrl,
},
})
The important question is:
What is callbackUrl on this deployment?
Locally, it may be:
http://localhost:3000/auth/callback
In Production:
https://www.example.com/auth/callback
In Preview:
https://feature-branch-your-team.vercel.app/auth/callback
If the generated URL is not on the Supabase Redirect URLs list, the flow may not return where you expect.
7. Use the deployment URL intentionally
Supabase's current Vercel guidance mentions using a production site URL together with Vercel's deployment URL variables for environment-aware redirects.
A common approach is to prefer:
- your explicit production site URL
- the current Vercel deployment URL
- localhost as the development fallback
The exact implementation depends on your framework and application.
The principle is more important:
Do not hard-code localhost into a redirect helper that also runs in Production.
And do not assume a generated Preview hostname is identical to your production hostname.
8. Check session and cookie handling
If the redirect appears correct but the user is immediately treated as logged out, the problem may be session handling rather than the redirect allow list.
In server-rendered Next.js applications, Supabase authentication often involves cookies so the server and browser can share session state.
Check:
- are auth cookies being written?
- are they available on the domain you expect?
- is server-side Supabase code reading them correctly?
- is middleware or a callback route refreshing the session where required?
- is any cache serving a response that should be user-specific?
Supabase's SSR guidance specifically warns about caching responses that contain refreshed session cookies, because cached authenticated responses can create serious session problems.
This is a different class of issue from environment variables.
Treat it separately.
How I separate the failure by symptom
Login button does nothing
Check:
- browser errors
- Supabase client configuration
- missing public variables
Login opens provider but returns to the wrong site
Check:
- Site URL
- Redirect URLs
redirectTo- Production vs Preview URL generation
Works in Production but not Preview
Check:
- Preview environment variables
- Preview redirect wildcard
- branch-specific configuration
- new deployment after changes
Works locally but email reset link returns to localhost
Check:
- Supabase Site URL
- explicit redirect target in the reset request
Redirect works but user appears logged out
Check:
- callback/session exchange
- cookies
- SSR client setup
- caching
- domain mismatch
This prevents me from treating every auth problem as the same bug.
A practical Next.js URL helper
The exact code varies by project, but the idea is to produce the correct base URL for the environment.
For example:
export function getSiteUrl() {
const explicit = process.env.NEXT_PUBLIC_SITE_URL
if (explicit) {
return explicit.endsWith("/") ? explicit : `${explicit}/`
}
const vercelUrl = process.env.NEXT_PUBLIC_VERCEL_URL
if (vercelUrl) {
return `https://${vercelUrl}/`
}
return "http://localhost:3000/"
}
Then the auth flow can construct the callback from that base URL.
This is an example pattern, not a requirement.
The key is to understand which URL the deployed app is generating.
Production redirect configuration
For the live application, I prefer explicit values.
Example:
Supabase Site URL
https://www.example.com
Allowed Production Redirect
https://www.example.com/**
Or an even narrower callback path if your application only needs one.
Exact rules are easier to reason about and safer to maintain.
Preview redirect configuration
For previews, a wildcard can be practical because every deployment may receive a generated hostname.
Supabase documents a Vercel Preview pattern like:
https://*-<team-or-account-slug>.vercel.app/**
Use your real team/account slug and verify that the pattern matches your deployment URLs.
Do not copy a placeholder pattern without checking it.
Do not expose Supabase secret keys to fix auth
When authentication fails, it can be tempting to try a more powerful key.
Do not solve browser authentication by placing a Supabase secret key or legacy service_role key in a NEXT_PUBLIC_ variable.
Public client authentication should use a publishable key.
Authorization and data security should be enforced with:
- authenticated user identity
- database grants
- Row Level Security
- trusted server-side code for privileged operations
Authentication troubleshooting should not weaken the application's security model.
My Supabase + Vercel login checklist
- [ ] The failing deployment uses the expected Vercel project.
- [ ] Supabase variable names match the application code.
- [ ] Preview has the variables required by Preview.
- [ ] Production has the variables required by Production.
- [ ] A new deployment was created after environment changes.
- [ ] Supabase Site URL is the real production URL.
- [ ] Local redirect URLs are allowed only where needed.
- [ ] Preview redirect patterns match the actual Vercel hostname.
- [ ] Production redirects use the intended production domain.
- [ ] The application's
redirectTovalue matches an allowed URL. - [ ] Session cookies work after the callback.
- [ ] Server-side auth code is not being incorrectly cached.
- [ ] No privileged secret is exposed to the browser.
FAQ
Why does Supabase login work locally but not on Vercel?
The deployed environment may be missing Supabase variables, using a different redirect URL, testing an old deployment, or handling sessions differently from localhost.
Why does Supabase redirect back to localhost?
Check the Supabase Site URL and the explicit redirectTo value used by the application. A production project should not rely on localhost as its default redirect.
How do I support Vercel Preview URLs in Supabase?
Add an appropriate wildcard pattern to Supabase Redirect URLs that matches your Vercel Preview hostname structure.
Why does Supabase login work in Production but not Preview?
Preview may have different environment variables and a different hostname. Check both the Vercel Preview variable scope and the Supabase redirect allow list.
Should I use a secret Supabase key for login?
No. Browser clients should use the publishable key. Secret or privileged keys belong only in trusted server-side code.
Final thoughts
The biggest change in my debugging process was learning not to call every failed login an authentication-code problem.
A deployed login flow crosses several systems:
Browser → application → Vercel environment → Supabase Auth → redirect URL → session → application again
A failure at any of those boundaries can look like "Supabase login is broken."
Now I check the boundaries one by one.
That is much faster than rewriting a working login flow blindly.
For the broader environment-variable troubleshooting process, read Vercel Environment Variables Not Working? 7 Supabase Fixes.
For the backend architecture behind authentication and permissions, see Why Use Supabase for a Web App? Auth, RLS & Real Project Lessons.
Practical AI. Real Experience. Built in Public.