Supabase confirmation and magic-link emails redirect to localhost after deploy
Updated 2026-11-11
What you see
A new user signs up on your live site, clicks the confirmation email, and lands on http://localhost:3000 with a long #access_token=… in the address bar. Or GitHub/Google login bounces back to localhost. Password reset links do the same.
Why it happens
Supabase Auth builds those links from your project’s Site URL whenever your code doesn’t pass a redirectTo. On a fresh project the Site URL is http://localhost:3000. Supabase’s docs single it out as the setting to change for production, because email confirmations and password resets depend on it.
If your code does pass redirectTo, that URL must also be on the project’s Redirect URLs allow list, or Supabase falls back to the Site URL.
Fix it
- In the Supabase dashboard, open Authentication → URL Configuration.
- Set Site URL to your real production URL, e.g.
https://yourapp.com. - Under Redirect URLs, add every URL you redirect to:
- your production URL:
https://yourapp.com/** - local development:
http://localhost:3000/** - Vercel previews:
https://*-<your-team-or-account-slug>.vercel.app/** - Netlify previews:
https://**--<your-site>.netlify.app/**
- your production URL:
- Pass
redirectToexplicitly in code so previews and production both work:
const getURL = () => {
let url =
process?.env?.NEXT_PUBLIC_SITE_URL ?? // set this in production
process?.env?.NEXT_PUBLIC_VERCEL_URL ?? // set automatically by Vercel
'http://localhost:3000/';
url = url.startsWith('http') ? url : `https://${url}`;
return url.endsWith('/') ? url : `${url}/`;
};
await supabase.auth.signInWithOAuth({
provider: 'github',
options: { redirectTo: getURL() },
});
For a Vite app (Lovable, Bolt), use window.location.origin or an import.meta.env.VITE_SITE_URL variable in place of the NEXT_PUBLIC_ ones.
- Email templates: if you rely on
redirectTo, Supabase notes you may need to replace{{ .SiteURL }}with{{ .RedirectTo }}in your confirmation and magic-link templates.
Gotchas
- Wildcards are strict about separators.
/*does not match nested paths like/auth/callback, so use/**. - Supabase recommends an exact redirect path in production rather than a broad
**pattern. - Links already sent still carry the old URL. Test with a fresh sign-up.
Check it worked
Sign up with a new email address on the live site. The confirmation link should open your production domain and leave you signed in.