Supabase Auth

Supabase Confirmation Email Redirects to localhost? Here's the Fix

8 min read · Updated October 16, 2026 · Checked against official docs

Your app is live. A real person signs up, clicks the link in the confirmation email, and lands on this:

This site can't be reached
localhost refused to connect.

Or the link works but drops them on your old preview URL instead of your real domain. Password reset and magic links do the same thing. This is one of the most common “it worked when I built it” problems in Lovable, Bolt and Cursor apps that use Supabase Auth, and it’s almost always configuration, not code.

The short version: Supabase only redirects to URLs you’ve told it about, and when it isn’t sure, it falls back to the Site URL, which starts out as http://localhost:3000. Below are the four places to check, in order.

When someone signs up, Supabase emails a link that points at Supabase itself (your https://<project>.supabase.co/auth/v1/verify?... address). Supabase verifies the token and then sends the browser on to a redirect URL. That final hop is chosen like this:

  1. If your code passed a redirect (emailRedirectTo or redirectTo) and that URL is on your Redirect URLs allow list, Supabase uses it.
  2. Otherwise, it uses the Site URL.

Supabase’s troubleshooting page puts it plainly: for the redirectTo option to work, you must set the exact URL in the Redirect URLs setting. (Supabase: Why am I being redirected to the wrong URL?)

So “redirects to localhost” usually means one of: the Site URL is still localhost, your redirect isn’t on the allow list, or your code is passing a localhost URL.

Check 1: the Site URL

In the Supabase dashboard, go to Authentication → URL Configuration.

Set Site URL to your real production address, with https:// and no trailing path:

https://yourdomain.com

Supabase’s docs call this setting critical for email confirmations and password resets, and tell you to change it from http://localhost:3000 to your production URL. (Supabase: Redirect URLs)

Two details people miss:

  • Use the domain people actually visit. If www.yourdomain.com redirects to yourdomain.com, use yourdomain.com. Sessions are stored per domain, so landing on the “wrong” one can look like being logged out.
  • Click Save, then send a new email. Links in emails you already received were built with the old settings and will keep pointing at localhost.

Check 2: the Redirect URLs allow list

On the same page, add every URL your app may send users back to. A sensible setup:

https://yourdomain.com/**
http://localhost:3000/**

Add your host’s preview URLs too if you test sign-in there. Supabase’s docs show patterns like https://*-<team-or-account-slug>.vercel.app/** for Vercel previews and https://**--my_org.netlify.app/** for Netlify.

How the wildcards behave, from the same docs:

PatternMatches
http://localhost:3000/*/foo, but not /foo/bar
http://localhost:3000/**/foo and /foo/bar

Supabase recommends ** for local development and previews, and the exact path for production. Once everything works, consider replacing https://yourdomain.com/** with the exact callback page your app uses, for example https://yourdomain.com/auth/callback.

If your local dev server isn’t on port 3000 (Vite defaults to 5173, for example), add that port instead. A mismatch here is a classic reason the allow list “doesn’t work”.

Check 3: what your code sends

Search your project for emailRedirectTo, redirectTo and localhost. AI tools often hard-code the URL they were running on:

// ❌ hard-coded, ships to production unchanged
await supabase.auth.signUp({
  email,
  password,
  options: { emailRedirectTo: 'http://localhost:3000/welcome' },
});

Build the URL from wherever the app is actually running instead:

// ✅ works locally, on previews and in production
await supabase.auth.signUp({
  email,
  password,
  options: { emailRedirectTo: `${window.location.origin}/welcome` },
});

The same idea applies to the other auth calls:

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

await supabase.auth.resetPasswordForEmail(email, {
  redirectTo: `${window.location.origin}/update-password`,
});

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

window.location.origin only exists in the browser. If the call runs on a server (Next.js server actions or API routes), use an environment variable with your public URL instead, set to https://yourdomain.com in production. Supabase’s redirect docs include a helper that does this for Vercel using NEXT_PUBLIC_SITE_URL. For a refresher on how those variables work, see Environment Variables and API Keys, Explained.

Remember: whatever URL you pass must also be on the allow list from Check 2, or Supabase quietly falls back to the Site URL.

Check 4: the email templates

Go to Authentication → Email Templates in the dashboard (the menu label can differ slightly between dashboard versions). Open each template you use: Confirm signup, Magic Link, Reset Password, Change Email, Invite.

Look at the link inside. The defaults use {{ .ConfirmationURL }}, which already respects your redirect. Problems start when a template (or a tutorial you copied) builds the link by hand:

<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email">Confirm</a>

That always goes to the Site URL, no matter what your code passes. If you want the link to follow your code’s redirect, Supabase’s docs say to use {{ .RedirectTo }} instead of {{ .SiteURL }}:

<a href="{{ .RedirectTo }}/auth/confirm?token_hash={{ .TokenHash }}&type=email">Confirm</a>

Also look for a literal http://localhost typed into a template. It happens. (Supabase: Email Templates)

Using Google, GitHub or other social logins?

Social sign-in has one more hop. The provider (Google, say) sends the user back to Supabase, and Supabase then sends them to your app. So you need:

  • In the provider’s console (Google Cloud, GitHub OAuth app): the Supabase callback URL shown on the provider’s page in your Supabase dashboard (it looks like https://<project>.supabase.co/auth/v1/callback). You don’t put your own domain here.
  • In Supabase URL Configuration: your app’s domain, as in Checks 1 and 2.

If you moved from your own Supabase project to a new one, the callback URL changed, and the provider console needs updating.

Using Lovable Cloud?

If your Lovable app uses Lovable’s built-in backend rather than your own Supabase project, the settings live in Lovable instead of the Supabase dashboard. Lovable’s docs say it adds your custom domain to the allowed redirect URLs when the domain goes live. If links still point to the wrong place, check the auth URL settings under More → Cloud, and see How to Publish a Lovable App on Your Own Custom Domain for the full domain checklist.

Still going to localhost? Debug it in two minutes

  1. Request a fresh confirmation or reset email after saving all settings.
  2. Don’t click the link. Long-press or right-click and copy it.
  3. Paste it somewhere you can read it and find the redirect_to= part. It’s URL-encoded, so https%3A%2F%2Fyourdomain.com means https://yourdomain.com.

What you see tells you where to look:

redirect_to showsLikely cause
http://localhost:...Your code passes localhost (Check 3)
Your real domain, but you still land on localhostThat URL isn’t on the allow list, so Supabase falls back to the Site URL (Checks 1 and 2)
No redirect_to, link goes straight to your domainA custom template builds the link (Check 4)

A prompt for your AI tool

Supabase auth emails (signup confirmation, magic link, password reset) send
users to localhost after deploy. Search the whole project for "localhost",
"emailRedirectTo" and "redirectTo". Replace hard-coded URLs with
`${window.location.origin}/<path>` in browser code, or with a SITE_URL
environment variable in server code. List every redirect path the app uses
so I can add them to Supabase's Redirect URLs allow list. Don't change
anything else.

Final test before you call it done

  1. Sign up with a brand-new email address in a private browser window on your real domain.
  2. Confirm the email. You should land on your domain, signed in.
  3. Sign out, use “forgot password”, and complete the reset.
  4. If you use magic links or Google sign-in, test those too.

While you’re in launch mode, run through the pre-launch security checklist. If you’re moving hosts at the same time and other pages break on refresh, the 404-on-refresh guide covers that.


Official sources used: Supabase: Redirect URLs · Supabase: Wrong redirect URL troubleshooting · Supabase: Email Templates · Supabase: Passwordless email logins. Checked October 2026.

Want the whole launch path in one place?

The Ship It Kit bundles copy-paste prompts, step-by-step deploy guides and a pre-launch checklist for Lovable, Bolt, Cursor and Replit apps. One-time $19. Launch price: $19 $17 until November 14, 2026.

Get the Ship It Kit, $19$17 See what's inside

Spotted something out of date? Platforms change their screens often. The official docs linked above are always the source of truth.