Supabase Google OAuth redirects to localhost after login: how to fix it
If Google sign-in succeeds but your live site sends users to http://localhost:3000, the usual cause is that Supabase's Site URL is still the default http://localhost:3000 and your redirectTo is missing, not on the Redirect URLs list, or itself set to localhost.
Set the Site URL to your production domain, pass redirectTo in signInWithOAuth, and add that exact URL to Supabase's Redirect URLs list.
Why production logins land on localhost
After Google hands the user back to Supabase Auth, Supabase decides where to send them next. Three things feed that decision:
- Site URL. Supabase's docs describe it as the default redirect when your code passes no
redirectTo. On new projects (and in the CLI'sconfig.toml) it starts ashttp://localhost:3000. If you never changed it, every login withoutredirectToends on localhost. redirectTothat isn't on the allow list. Supabase's own troubleshooting note on "redirects going to localhost instead of the production site URL" saysredirectToonly works if the exact URL is in the Redirect URLs setting. Supabase's docs don't spell out what happens to an unlisted URL, but the note ties this mistake to the localhost symptom: if the URL isn't listed, users may end up on the Site URL instead.redirectTothat is itself localhost. A hardcodedhttp://localhost:3000/..., or an environment variable such asNEXT_PUBLIC_SITE_URLthat was set to localhost when the production bundle was built, sends users there even when everything in Supabase is right.
Google Cloud is not involved in this hop. Google's authorized redirect URI is the Supabase callback (https://<project-ref>.supabase.co/auth/v1/callback), so adding your production domain to Google's redirect URIs won't change where Supabase sends the user.
Step-by-step fix
1. Change the Site URL
In the Supabase dashboard, open Authentication → URL Configuration (as of Oct 2026) and replace http://localhost:3000 with your production origin:
https://example.com
Supabase notes this setting also drives email confirmations and password resets, so it should always point at production.
2. Allow-list the exact production URL
On the same page, under Redirect URLs, add the URL your app will pass as redirectTo, exactly as the browser will request it (same scheme, www or not, path):
https://example.com/auth/callback
Keep a separate entry for local development so localhost keeps working:
http://localhost:3000/**
3. Always pass redirectTo, built from the current origin
Don't rely on the Site URL fallback. Build redirectTo from where the user actually is:
const { error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: `${window.location.origin}/auth/callback`,
},
})
If you compute the URL from environment variables instead (common with Next.js on Vercel), use a helper that prefers your production URL and normalizes the format. This is the pattern from Supabase's redirect URL guide:
const getURL = () => {
let url =
process?.env?.NEXT_PUBLIC_SITE_URL ?? // set to your production URL in the production env
process?.env?.NEXT_PUBLIC_VERCEL_URL ?? // Vercel system environment variable
'http://localhost:3000/'
url = url.startsWith('http') ? url : `https://${url}`
url = url.endsWith('/') ? url : `${url}/`
return url
}
await supabase.auth.signInWithOAuth({
provider: 'google',
options: { redirectTo: `${getURL()}auth/callback` },
})
NEXT_PUBLIC_* values are inlined at build time in Next.js, so after changing one in your host's settings, redeploy so the new value is in the bundle.
Test this on a preview deployment before relying on it.
4. Check the callback route's final redirect (SSR / PKCE)
If you use a server callback such as app/auth/callback/route.ts, its last redirect decides the final page. Behind a proxy or load balancer, request.url can carry an internal origin. Supabase's example reads x-forwarded-host for this reason:
const forwardedHost = request.headers.get('x-forwarded-host')
const isLocal = process.env.NODE_ENV === 'development'
if (isLocal) return NextResponse.redirect(`${origin}${next}`)
if (forwardedHost) return NextResponse.redirect(`https://${forwardedHost}${next}`)
return NextResponse.redirect(`${origin}${next}`)
Whether your host sets x-forwarded-host depends on its proxy. Test this on a preview deployment before relying on it.
How to confirm it worked
- Open your production site in a private window and click "Sign in with Google".
- Before choosing an account, read the Google URL: its
redirect_urishould be your Supabase callback, not your app. - After choosing an account you should land on
https://example.com/auth/callback(then your app), notlocalhost. - Repeat on
http://localhost:3000to make sure local development still lands locally.
Related problems
- Google shows
redirect_uri_mismatchinstead of redirecting at all: that is the Google → Supabase hop. See redirect_uri_mismatch. - Preview deployments go to production or fail: see the wildcard patterns in Supabase redirect URLs explained.
- Setting up from scratch: Supabase Google OAuth setup.
- Using Firebase rather than Supabase and seeing localhost errors: auth/unauthorized-domain on localhost.
FAQ
Why does Supabase redirect to localhost:3000 after Google login?
Usually because the Site URL is still http://localhost:3000 and your request either had no redirectTo or used a redirectTo that isn't in the Redirect URLs list. Change the Site URL to production and allow-list the exact redirectTo.
Do I need to add my production domain to Google Cloud's redirect URIs?
No. With Supabase, Google redirects to the Supabase callback (https://<project-ref>.supabase.co/auth/v1/callback), and that is the only redirect URI Google needs. Your production URL goes into Supabase's Site URL and Redirect URLs.
I changed the Site URL but users still land on localhost. What now?
Check what your code sends as redirectTo. A hardcoded localhost URL, or a localhost value baked into a production build through an environment variable, overrides the Site URL. Rebuild after fixing the variable.
Can I keep localhost working while production is fixed?
Yes. Keep the Site URL on production and add http://localhost:3000/** to Redirect URLs, then pass redirectTo built from window.location.origin so each environment returns to itself.
Sources
How this page was made: drafted with AI from the sources listed, reviewed by Pewterkit. Docs checked Oct 27, 2026. Spotted something out of date? Tell us.