Google login redirects to localhost or loops back to login? Fix Lovable and Supabase sign-in after deploy
Sign in with Google worked perfectly in the preview. Now that the app is live, users click the button, pick their Google account and land on localhost:3000, on a Google redirect_uri_mismatch error, or right back on your login page as if nothing happened. The cause is almost always one of a few URL settings that still point at the old address, or a session that gets lost on the way back. Work through these steps in order; most apps are fixed by step 4.
- Work out which failure you have
- Lovable Cloud apps: check Auth settings first
- Your own Supabase project: fix Site URL and Redirect URLs
- Google Cloud: the redirect URI is Supabase's callback, not your site
- Send redirectTo from the current origin, never a hardcoded URL
- Pick one domain: www and non-www are different sites
- Login loop in a React/Vite app: wait for the session before redirecting
- Next.js and other SSR apps: exchange the code in a callback route
- Test the right way: published URL, new private window
- FAQ
1. Work out which failure you have
Open your live site in a private window, open DevTools → Network, tick Preserve log, then click Sign in with Google. A healthy flow goes: your app → <project-ref>.supabase.co/auth/v1/authorize → accounts.google.com → <project-ref>.supabase.co/auth/v1/callback → back to your app. Where it breaks tells you which step to read:
- You end up on
localhost:3000or an old preview address: Site URL or Redirect URLs (steps 2 and 3). - Google shows "Error 400: redirect_uri_mismatch": the Google OAuth client settings (step 4).
- Google says "Access blocked" or "Error 403: access_denied": the consent screen is still in testing (step 9).
- You come back to your site but you're logged out or bounced to /login: the session is lost after the redirect (steps 6–8).
- The URL ends with
error=anderror_description=: read the description; Supabase puts the real reason there.
2. Lovable Cloud apps: check Auth settings first
If your app uses Lovable's built-in backend (Cloud), open More → Cloud → Users → Auth settings → Advanced. According to Lovable's docs, publishing sets the Site URL to your lovable.app address and connecting a custom domain adds it to Redirect URLs. But a Site URL you (or an earlier prompt) set by hand is left unchanged, so an old preview or localhost value can stick around. Make the Site URL your real live address and confirm every domain you use is in Redirect URLs (up to 50 are allowed).
- Managed by Lovable Google sign-in (the default) needs no Google Cloud setup and no change when you add a domain.
- Your own credentials: when you connect a custom domain, Lovable adds a redirect URL for it under Auth settings → Google but does not select it. Until you add that URL in Google Cloud Console, select it in Lovable and click Save, Google sign-in on the new domain fails with a redirect URI mismatch.
You can also ask in the project chat: "Update the auth Site URL and Redirect URLs for my published domain https://yourdomain.com."
3. Your own Supabase project: fix Site URL and Redirect URLs
Apps connected to your own Supabase project get no automatic updates. In the Supabase dashboard go to Authentication → URL Configuration:
- Site URL is where Supabase sends users when no allowed
redirectTois given. It defaults tohttp://localhost:3000, which is exactly why live users land on localhost. Set it to your production URL, e.g.https://yourdomain.com. - Redirect URLs is the allow list. If the
redirectToyour code sends isn't on it, Supabase ignores it and uses the Site URL instead.
https://yourdomain.com/**
https://www.yourdomain.com/**
https://yourapp.lovable.app/**
http://localhost:5173/**
Wildcards follow glob rules where . and / are separators: * stops at a slash, ** matches any path. So https://yourdomain.com/* does not match /auth/callback. Supabase recommends exact paths for production; ** is handy for localhost and preview URLs (for Vercel previews, a pattern like https://*-yourteam.vercel.app/**). Click Save.
4. Google Cloud: the redirect URI is Supabase's callback, not your site
Only needed if you use your own Google OAuth client (your own Supabase project, or Lovable with your own credentials). In Google Cloud Console open Google Auth Platform → Clients (formerly APIs & Services → Credentials) and edit the Web application client:
- Authorized redirect URIs: your Supabase callback, copied from the Google provider page in the Supabase dashboard:
https://<project-ref>.supabase.co/auth/v1/callback. Google sends users back to Supabase, and Supabase then sends them to your app. Puttinghttps://yourdomain.comhere is a common mistake. - Authorized JavaScript origins: your app's address(es), e.g.
https://yourdomain.com. Removehttp://localhostentries you no longer need in production. - Exact match: scheme, domain, path and trailing slash all count.
httpvshttpsor a stray/causes redirect_uri_mismatch. - Supabase custom domain (e.g.
api.yourdomain.com): once it's activated, OAuth flows use it as the callback, so addhttps://api.yourdomain.com/auth/v1/callbackin addition to thesupabase.coone.
Then make sure the same Client ID and secret are saved under Authentication → Sign In / Providers → Google in Supabase. Google can take a few minutes to apply changes, so wait before retesting.
5. Send redirectTo from the current origin, never a hardcoded URL
AI-generated code often hardcodes the preview URL or localhost. Search the project for localhost, lovableproject and redirectTo. Build the URL from where the user actually is, and make sure that path is in Redirect URLs:
await supabase.auth.signInWithOAuth({
provider: 'google',
options: { redirectTo: `${window.location.origin}/auth/callback` },
})
// Email sign-up and password reset links have the same problem:
await supabase.auth.signUp({
email, password,
options: { emailRedirectTo: `${window.location.origin}/welcome` },
})
await supabase.auth.resetPasswordForEmail(email, {
redirectTo: `${window.location.origin}/reset-password`,
})
If you pass redirectTo and your email templates link to {{ .SiteURL }}, switch them to {{ .RedirectTo }} (Authentication → Email Templates) so the email link matches. On Lovable Cloud, the generated sign-in code may use Lovable's own auth helper; ask Lovable to fix the redirect rather than swapping in hand-written code.
6. Pick one domain: www and non-www are different sites
Browsers keep localStorage and cookies per origin. If login starts on https://www.yourdomain.com but Supabase sends the user back to https://yourdomain.com, the new origin has no session and no PKCE code verifier, so the user looks logged out or gets a code-exchange error. The same happens between your lovable.app address and your custom domain.
- Choose one canonical address and redirect the other to it at your host or DNS level.
- Use that canonical address as the Site URL.
- Build
redirectTofromwindow.location.origin(step 5) so users always come back to the origin they started on.
7. Login loop in a React/Vite app: wait for the session before redirecting
If Google and Supabase both succeed but you land back on /login, a route guard is usually checking for a user before supabase-js has finished reading the session from the URL. It sees "no user", redirects to /login, and the cycle repeats. Track a loading state and listen for the INITIAL_SESSION event:
const [session, setSession] = useState(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
const { data: { subscription } } = supabase.auth.onAuthStateChange((event, session) => {
setSession(session)
if (event === 'INITIAL_SESSION') setLoading(false)
})
return () => subscription.unsubscribe()
}, [])
if (loading) return <Spinner /> // don't redirect yet
if (!session) return <Navigate to="/login" />
- Create the Supabase client once (one shared module) and import it everywhere; several clients can fight over the same session.
- Keep the
onAuthStateChangecallback synchronous. Awaiting other Supabase calls inside it can stall auth; fetch the profile in a separate effect. - Don't strip
?code=or the#access_tokenhash from the URL before the client has processed it (detectSessionInUrlis on by default in the browser). - If the session is fine but the profile query returns nothing, the app may treat you as logged out. That's row-level security; see Supabase returns an empty array?
8. Next.js and other SSR apps: exchange the code in a callback route
Server-rendered apps using @supabase/ssr use the PKCE flow: Supabase sends the user to your redirectTo with ?code=..., and your server must exchange it for a session that's saved in cookies. The minimal Next.js App Router version of Supabase's callback route:
// app/auth/callback/route.ts
import { NextResponse } from 'next/server'
import { createClient } from '@/utils/supabase/server'
export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url)
const code = searchParams.get('code')
let next = searchParams.get('next') ?? '/'
if (!next.startsWith('/')) next = '/'
if (code) {
const supabase = await createClient()
const { error } = await supabase.auth.exchangeCodeForSession(code)
if (!error) return NextResponse.redirect(`${origin}${next}`)
}
return NextResponse.redirect(`${origin}/auth/auth-code-error`)
}
- "PKCE code verifier not found in storage": the verifier saved when login started isn't available. Login must start and finish in the same browser and origin, and both your browser and server clients must come from
@supabase/ssrso the verifier lives in cookies. - Each code works once and expires after 5 minutes, so a refresh or double-submit of the callback fails. Start login again.
- Make sure your middleware or proxy doesn't redirect
/auth/callbackto/loginfor signed-out users, or the exchange never runs. - On Vercel, set
NEXT_PUBLIC_SITE_URLto your production URL and use it when buildingredirectToon the server. More deployment checks: App works locally but not on Vercel?
9. Test the right way: published URL, new private window
Google doesn't let its sign-in page load inside an iframe, and an editor preview often runs in one, so testing Google login inside the editor can fail even when the live app is fine. Always test on the published URL in a fresh private window. Also:
- If your Google consent screen is still in Testing, only listed test users can sign in; everyone else sees "Access blocked" or
403: access_denied. Add testers or publish the app under Google Auth Platform → Audience. - Supabase needs the
openid,userinfo.emailanduserinfo.profilescopes on your Google client's Data Access screen. - If you use Google One Tap or the pre-built button instead of the redirect flow, set
data-use_fedcm_for_prompt="true"as Supabase recommends, so it keeps working as browsers phase out third-party cookies. - After changing redirect settings, clear the site's storage (DevTools → Application → Clear site data) so an old broken session doesn't confuse the test.
Still looping? Send us your live URL and a screenshot of the Network redirect chain from step 1 through the free check form or by email to fix@getvibefix.com. We'll tell you exactly which setting is wrong for free, before you pay anything.
FAQ
Why does Supabase redirect to localhost:3000 after Google login?
The project's Site URL is still the default http://localhost:3000, or the redirectTo URL your code sends isn't on the Redirect URLs allow list, so Supabase falls back to the Site URL. Set Authentication → URL Configuration → Site URL to your live domain and add your live callback URL to Redirect URLs.
What redirect URI do I add in Google Cloud Console for Supabase?
Your Supabase project's callback URL, https://<project-ref>.supabase.co/auth/v1/callback (shown on the Google provider page in the Supabase dashboard), not your app's own URL. Your app's domain goes under Authorized JavaScript origins. If you use a Supabase custom domain, add its /auth/v1/callback URL as well.
Do I need to set up Google Cloud Console for a Lovable Cloud app?
Not with the default Managed by Lovable Google sign-in: Lovable handles the OAuth client and redirect URLs. Only if you switched to Your own credentials do you need to add each redirect URI Lovable shows (including new custom domains) in Google Cloud and select them in Lovable.
Why does Google login work in preview but not on my custom domain?
The custom domain is missing from the auth Redirect URLs, or, with your own Google credentials, from the Google OAuth client's authorized redirect URIs. Lovable Cloud adds custom domains to Redirect URLs automatically, but a Site URL you set yourself is never changed, and own-credentials setups need the new redirect URI selected and saved.
What does "PKCE code verifier not found in storage" mean?
The callback tried to exchange the ?code= for a session, but the code verifier saved when login started isn't there. Login must start and finish in the same browser on the same domain (www and non-www count as different), and server-rendered apps must use @supabase/ssr on both client and server so the verifier is stored in cookies.
Still stuck? We'll fix it at a flat price.
VibeFix fixes broken Lovable, Bolt, Cursor, Replit and v0 apps. Request a free check first, and we'll tell you what's wrong and which plan fits before you pay. If we can't fix it, you get a full refund.
Already know you need help? Quick Fix, $49 (one bug, 24h) · Standard Rescue, $129 · Deploy-Ready, from $199