HeHeedza

Why does Google login redirect to localhost after I deploy?

Last reviewed

Short answer

Four settings have to agree: the Supabase Site URL, the Supabase Redirect URLs, your Google OAuth client, and the redirectTo in your code. If any one still points at localhost or a preview URL, Google sign-in breaks on the live site. Fix all four, then test in a private window.

Why this happens

Google sign-in through Supabase makes three hops: your app → Google → Supabase's callback → your app. Each hop has its own rule:

  • Google → Supabase: Google only redirects to URIs listed on your OAuth client. That URI is Supabase's callback, https://<project-ref>.supabase.co/auth/v1/callback, not your app's URL. A wrong value here gives Error 400: redirect_uri_mismatch.
  • Supabase → your app: Supabase only returns to URLs on its Redirect URLs list. If the URL isn't listed, it falls back to the Site URL, which defaults to http://localhost:3000.
  • Your code: if redirectTo is hardcoded to localhost or a preview domain, users go there even when the dashboard settings are right.

People often fix one or two of these, test in a browser that still has an old session, and think it works.

How to fix it, step by step

1. Supabase: Site URL. Go to Authentication → URL Configuration and set it to https://yourdomain.com.

2. Supabase: Redirect URLs. Add https://yourdomain.com/**, plus https://www.yourdomain.com/** if you serve www. If you use a callback route, you can list its exact path instead, e.g. https://yourdomain.com/auth/callback.

3. Google Cloud Console (Google Auth Platform → Clients → your Web client):

  • Authorized JavaScript origins: https://yourdomain.com
  • Authorized redirect URIs: https://<project-ref>.supabase.co/auth/v1/callback. Copy it exactly from Authentication → Providers → Google in Supabase. If you've set up a Supabase custom domain, use that host instead.

Remove the http://localhost origins once you're in production, as Google's setup notes for Supabase recommend.

4. Your code. Build redirectTo from where the user actually is:

await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: { redirectTo: `${window.location.origin}/auth/callback` },
});

With server-side auth (Next.js and @supabase/ssr), the callback route must exchange the code:

// 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');
  if (code) {
    const supabase = await createClient();
    const { error } = await supabase.auth.exchangeCodeForSession(code);
    if (!error) return NextResponse.redirect(`${origin}/`);
  }
  return NextResponse.redirect(`${origin}/auth/auth-code-error`);
}

5. Lovable Cloud users: with Managed by Lovable Google sign-in, Lovable handles the Google side. With your own credentials, add every redirect URI Lovable lists under More → Cloud → Users → Auth settings → Google to your Google client, then select them in Lovable and click Save.

6. Test properly. Use a private window, sign out of any old session first, and test on the live domain, not the preview.

Fix prompt for your AI builder

Paste this into Lovable, Bolt, Cursor, Claude, ChatGPT, Replit, v0 or whichever tool you build with. Replace the bracketed parts first.

Fix prompt
Google sign-in on https://yourdomain.com sends users to localhost (or fails) after deploy. Fix only the OAuth redirect:
1. Change signInWithOAuth so redirectTo is `${window.location.origin}/auth/callback` (or our existing callback path), with no hardcoded localhost or preview URLs.
2. Make sure the callback route exists and exchanges the code for a session, then sends the user to the home page.
3. Give me a checklist of the Supabase Site URL, the Redirect URLs, and the Google Cloud authorized origin and redirect URI I need for this domain.
Don't touch the UI, other providers or the database.

How to catch this next time

Google OAuth breaks again when someone adds a domain, rotates the OAuth client, or regenerates the auth code. Write the four settings down next to your deploy notes.

Heedza checks that your live login page loads and shows its sign-in form on a schedule, and it emails a screenshot and a fix prompt when it doesn't. It doesn't sign in through Google for you, so after any domain change also do one private-window Google sign-in. You can see what Heedza checks on your site with the free scan.

Questions

Which redirect URI goes in Google Cloud: my app or Supabase?

Supabase's callback, https://<project-ref>.supabase.co/auth/v1/callback. Your app's URL goes in Supabase's Redirect URLs and in Google's Authorized JavaScript origins.

What causes Error 400: redirect_uri_mismatch?

The redirect URI Google received doesn't exactly match one on your OAuth client. Check the scheme, host, path and trailing slash.

It works for me but not for new users. Why?

You may be testing with an existing session or a cached redirect. Test in a private window while signed out. If your Google app's audience is set to Testing, only listed test users can sign in.

Sources