Login works on the Lovable preview. You publish, or put a custom domain in front, and now sign-in loops, drops you on localhost:3000, or comes back with no session. The React didn't change. The host did.
Supabase Auth only sends the browser back to URLs it already trusts. Preview was on that list. Production wasn't.
What it looks like
- Preview: Google / GitHub / magic link works. Production: spinner, then the login page again.
- After OAuth you land on
http://localhost:3000or the old*.lovable.apppreview host. - The address bar ends in
#error=...orerror_description=Unsupported%20redirect. - Google shows
Error 400: redirect_uri_mismatchnaming a URL you don't recognize. - Confirm / reset emails open the preview app, or 404 on the live domain.
Test in a private window on the live origin. Preview cookies will lie to you.
There are three lists, not one
Most people paste the production URL into one box and call it done.
-
Supabase Site URL (Authentication → URL Configuration). The default redirect when your code doesn't pass
redirectTo. Magic links and password resets use it. A new project starts athttp://localhost:3000. -
Supabase Redirect URLs (same page). The allowlist. Every
redirectTomust match it, or Auth quietly ignores it and uses Site URL. That's how you "set production" and still bounce to localhost. -
The OAuth provider's Authorized redirect URI (Google Cloud, GitHub). This is not your app. It's Supabase's callback,
https://YOUR_PROJECT.supabase.co/auth/v1/callback. Publishing the Lovable app doesn't change it. Getting it wrong givesredirect_uri_mismatchat the provider, which looks like a Lovable bug.
On a connected Supabase project, the first two live in the Supabase dashboard, not the Lovable chat. (Lovable Cloud, the built-in backend, is configured in Lovable instead.)
The fix
Site URL = the live origin, e.g. https://myapp.com (or your *.lovable.app host if that's what's published). Leave preview out of it, since it's the default for emails.
Redirect URLs: add every origin you actually use. ** matches across /:
https://myapp.com/**
https://myapp.com/auth/callback
https://*.lovable.app/**
http://localhost:5173/**
Whatever path your code passes as redirectTo must be covered. A trailing-slash mismatch is enough to fall back to Site URL.
Provider console (Google / GitHub): JavaScript origins get your app's origin. Authorized redirect URIs get the Supabase callback copied from Authentication → Providers. Don't put https://myapp.com/auth/callback there: Google returns to Supabase, and Supabase returns to your app.
redirectTo in code: hardcoding the preview host is how production "randomly" returns to preview. Use the current origin:
const redirectTo = `${window.location.origin}/auth/callback`;
const { error } = await supabase.auth.signInWithOAuth({
provider: "google",
options: { redirectTo },
});
Same for resetPasswordForEmail and signInWithOtp. If you customized email templates, {{ .SiteURL }} always points at Site URL; use {{ .RedirectTo }} when the link should follow what you passed.
60-second checklist
- Private window, live hostname.
- Site URL is production, not localhost or only the preview host.
- Redirect URLs cover production (plus
/auth/callbackif you use it); preview can stay as a wildcard. - Provider redirect URI is
https://YOUR_PROJECT.supabase.co/auth/v1/callback. -
redirectTois built fromwindow.location.origin. - Read the
error/error_descriptionin the URL fragment on the landing page. Auth still redirects on failure; the reason is in the hash.
Full guide
The longer version with source links and an FAQ (Site URL vs Redirect URLs, "I set Site URL, why localhost?", Lovable vs Supabase settings) is here:
https://fixloop.dev/guides/lovable-login-preview-vs-production
If sessions exist but saves silently do nothing, that's RLS, not redirects. I wrote up a 5-minute check for that.
I run FixLoop. There's a free scan for leaked keys and missing headers, and we fix broken Lovable/Supabase apps for a fixed price. No fix, no pay.
Top comments (0)