Fix guides

Supabase login redirects to localhost after deploy

Google sign-in, magic links, or confirmation emails send users to localhost or back to the login page. How to fix Supabase auth redirect URLs in a Lovable, Bolt, or Cursor app.

What you are seeing

Why it happens

  1. The Site URL in Supabase is still the default http://localhost:3000. Supabase falls back to it whenever no redirect is passed or the one passed is not allowed.
  2. The URL you redirect to is not in the Redirect URLs allow list, so Supabase ignores it and uses the Site URL.
  3. The AI hardcoded redirectTo: 'http://localhost:3000' while building locally.
  4. The preview and production domains differ and only one of them is allowed.
  5. With the PKCE flow (Next.js with @supabase/ssr), the callback route never exchanges the code for a session, so the user looks signed out.
  6. Supabase's built-in email sender is for testing only and is limited to 2 emails per hour (per Supabase's docs), so real signups hit the limit fast.

How to fix it

1. Open the auth URL settings

In the Supabase dashboard, open Authentication, then URL Configuration.

2. Set the Site URL and allow every domain you use

Site URL:       https://yourapp.com
Redirect URLs:  https://yourapp.com/**
                https://*-yourteam.vercel.app/**
                http://localhost:3000/**

3. Find hardcoded localhost redirects

grep -rnE "localhost:(3000|5173|8080)" src app lib 2>/dev/null

4. Redirect to the current origin, not a fixed URL

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

await supabase.auth.signUp({
  email,
  password,
  options: { emailRedirectTo: `${window.location.origin}/auth/callback` },
});

5. Exchange the code for a session (Next.js App Router)

// app/auth/callback/route.ts
import { NextResponse } from 'next/server';
import { createClient } from '@/lib/supabase/server';

export async function GET(request) {
  const { searchParams, origin } = new URL(request.url);
  const code = searchParams.get('code');
  const next = searchParams.get('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}/login?error=auth`);
}

6. Check the Google side

In Google Cloud Console, the OAuth client's authorized redirect URI must be your Supabase callback, https://<project-ref>.supabase.co/auth/v1/callback, not your app's URL. Your app's own URLs belong in Supabase's Redirect URLs list.

7. Use your own email sender before launch

Add a custom SMTP provider in Supabase's Authentication settings so confirmation and reset emails are not capped by the built-in sender.

How to know it is fixed

Open the live site in a private window, sign in with Google, and confirm you land on your own domain signed in. Then request a password reset and check that the link in the email points at your domain, not localhost.