Supabase email links go to localhost: why confirmation and reset links break, and how to fix them
Change the Site URL, allow your redirect pages, and pass a redirect address the project accepts

Your app is live, someone signs up, and the confirmation email sends them to localhost:3000, which their browser can't reach ("This site can't be reached"). The password reset email does the same thing. The cause is one project setting, and the fix takes a few minutes.
Where the link comes from
Supabase builds the links in auth emails from your project's Site URL. New projects start with it set to http://localhost:3000. Three things send users there:
- The Site URL was never changed. With no other instruction, every link falls back to it.
- The app redirects to an address Supabase doesn't allow. A redirect that isn't on the Redirect URLs list is dropped, and the Site URL is used instead.
- The email template links with
{{ .SiteURL }}. A customised template that uses it ignores the redirect your code passed. For redirect flows the template should use{{ .RedirectTo }}.
Work out which one you have
Hover the button in the email and read the address. Then open Authentication > URL Configuration in the Supabase dashboard and compare it with the Site URL and the Redirect URLs list. Last, look at the calls in your code to signUp, resetPasswordForEmail and signInWithOtp, and note the redirect address each one passes.
Fix the project settings
Set the Site URL to the address your users actually use, such as https://app.example.com. Under Redirect URLs, add the exact pages your auth flows land on, for example /auth/callback and /reset-password on that domain. You can keep http://localhost:3000/** for local development, but in production the Supabase docs recommend exact paths over wildcards.
Pass the redirect from your code
When the redirect is built from the current origin, the same code works on previews and in production:
await supabase.auth.signUp({
email,
password,
options: { emailRedirectTo: window.location.origin + '/auth/callback' },
});
await supabase.auth.resetPasswordForEmail(email, {
redirectTo: window.location.origin + '/reset-password',
});
Each origin you run on still has to match an entry on the Redirect URLs list.
Update custom templates
If you edited the email templates, open Authentication > Email Templates and swap {{ .SiteURL }} for {{ .RedirectTo }} wherever the flow passes a redirect address.
Prove it's fixed
- Sign up with an email address you haven't used before. The link should open your live site and sign you in.
- Ask for a password reset. The link should open the reset page on your live domain.
- If you have a preview or staging site, try once there too, so you know its pattern is allowed.
Keep it fixed
Both settings belong to each Supabase project, so add them to your launch checklist for every new one. And set up a custom sending domain for auth emails before launch, so the emails land in inboxes in the first place.
Two questions I get: you don't need to redeploy after changing the Site URL, because new emails pick it up straight away (old emails keep their old links). For a mobile app, use a deep link such as com.example.app://login-callback as the redirect and add it to the Redirect URLs list.
Source: Supabase: Redirect URLs.
This fix first appeared on our site: Supabase confirmation or reset email links to localhost. If you want a quick outside look at your live app (exposed keys, security headers, what Google can read), the free Launch Check takes about a minute.
I wrote this with AI assistance and reviewed it myself.
About the author
- Founder and CEO of StacksMesh (stacksmesh.com). We fix, launch and migrate apps built with Base44, Lovable, Bolt and Replit, with a fixed price in writing before any work. I write about making AI-built apps safe and production-ready.
Comments
Join the discussionNo comments yet. Be the first to comment.

