Supabase confirmation emails not arriving? Why it works for you and fails for real users
Your Supabase signup works when you test it, but real users never get the confirmation email. Here are the usual causes (default email service limits, spam, link scanners, wrong redirect) and how to check and fix each one.
You built the app with Lovable, Bolt, Replit, v0 or Cursor, wired up Supabase Auth, signed up with your own address, clicked the confirmation link, and everything worked. Then real people start signing up, and some of them write to say they never got the email. Or worse, they don't write at all. They just leave.
This is one of the most common ways a signup breaks without anyone noticing, because the person most likely to test it (you) is the one person for whom it keeps working. Below are the usual causes, how to check each one, and how to fix it.
Why it works for you and not for them
Supabase ships with a built-in email service so that auth works out of the box. It is meant for development, not for real users, and Supabase says so in its docs. Two of its limits explain most "works for me" reports:
- It only sends to your team. The default service delivers only to addresses that belong to members of your Supabase project or organization. Your own email gets through, so your test passes. A stranger's address is rejected, and the signup request can fail with an error like "Email address not authorized".
- It has a very low hourly rate limit. Once that limit is reached, further auth emails (confirmations, magic links, password resets) are refused with an error such as "Email rate limit exceeded" until the window resets. You can see the current limit in your project's Auth rate limit settings. A handful of signups in an hour, or one person pressing "resend" a few times, can be enough to hit it.
If your app was generated by an AI tool, it very likely still uses this default service, because nothing in the generated code changes it. The SMTP setting lives in the Supabase dashboard, not in your repository.
How to check what is actually happening
Before changing anything, find out which problem you have.
- Sign up with an address that is not on your Supabase team. Use a fresh Gmail or Outlook address you control. This is the single most useful test, because it is the path a real user takes.
- Look at the Auth logs in Supabase. The project logs show auth requests and their errors. Look for rate limit errors or "not authorized" errors around the time of the failed signups.
- Check the user record. In the Authentication users list, a user who signed up but never confirmed shows no confirmation time. If you see several of those, people are getting stuck at this step.
- Check your app's error handling. Many generated signup forms show "Check your inbox!" no matter what the API returned. If Supabase answered with an error, the user was still told everything worked. Open the browser's network tab, sign up, and look at the actual response from the signup request.
- Check spam and promotions folders on the test inbox. "Not arriving" and "arriving in spam" look the same to the user.
Fix 1: Use your own SMTP provider
This is the real fix for both the team-only restriction and the low rate limit. Sign up with a transactional email provider (Resend, Postmark, Amazon SES, SendGrid, Mailgun and similar services all work), verify your sending domain there, and enter the SMTP host, port, username and password in your Supabase project's SMTP settings.
A few details that save time:
- Send from your own domain, for example
hello@yourapp.com, not from a free mailbox. Free mailbox senders tend to land in spam or get rejected. - After switching, review the auth email rate limit in Supabase. With custom SMTP you can raise it to something that fits your real signup volume.
- Send yourself a test to an outside address right after saving. A typo in the SMTP password fails silently from the user's point of view.
Fix 2: Make sure the emails don't land in spam
Once mail is actually sent, deliverability is the next problem. Inbox providers trust mail from a domain that proves it is allowed to send.
- SPF and DKIM: your email provider gives you DNS records to add. Add them all and wait until the provider shows the domain as verified.
- DMARC: add at least a basic DMARC record for the domain. Gmail and Yahoo expect it from bulk senders, and it helps everyone else too.
- Keep the email plain. The default confirmation template is fine. If you customized it, avoid link shorteners, huge images or a subject line that reads like marketing.
You can check whether a message passed SPF, DKIM and DMARC by opening it in Gmail and choosing "Show original".
Fix 3: The link arrives but says it is invalid or expired
Sometimes the email does arrive, the user clicks it, and they see an error that the link is invalid or has expired. Two common reasons:
- Email security scanners open the link first. Some corporate mail systems (Microsoft's Safe Links is a well-known example) visit every link in an incoming message to check it for malware. A confirmation link usually works only once, so the scanner uses it up before the human clicks. Supabase's docs describe this problem. The usual fixes are to send a one-time code that the user types in instead of a link, or to have the link open a page in your app where the user presses a button to confirm, so a simple visit doesn't use the token.
- The user clicked an older email. If they requested the email twice, only the newest link is valid. Make your "expired link" page say that plainly and offer a resend button, instead of dropping them on a blank login page.
Fix 4: The link points to the wrong place
If the confirmation link opens localhost or an old preview URL, the Site URL or the allowed redirect URLs in Supabase's URL configuration still point to your development setup. Set the Site URL to your live domain, add your live domain to the redirect allow list, and make sure the emailRedirectTo value your app passes on signup (if any) uses the live domain too. Then sign up again with a new address, because emails that were already sent keep the old link.
Fix 5: Decide whether you need confirmation at all
Supabase has a setting that controls whether new users must confirm their email before they can sign in. If it is on, every delivery problem above becomes a hard block. Some apps turn it off and verify the email later, or only before sensitive actions. Others need it on to keep fake signups out. Either is a valid choice, but make it on purpose, and make sure your app's UI matches: if confirmation is required, the screen after signup should say "check your email" and offer a resend; if it is not, don't tell users to wait for an email that never comes.
A quick checklist
- Custom SMTP is configured, and a test email reaches an address outside your Supabase team.
- The auth email rate limit fits your real traffic.
- SPF, DKIM and DMARC pass for your sending domain.
- Site URL and redirect URLs point to the live domain.
- The signup form shows the real error when Supabase returns one.
- The expired-link page explains what happened and offers a resend.
- You test with a fresh outside address after every change to auth or email settings.
Keep checking after it's fixed
The annoying part about email confirmation is that it can break again later without any code change: a DNS record gets edited, an SMTP password is rotated, a rate limit is hit on a busy day. The only reliable way to know is to go through the signup the way a new user does, from a fresh address, regularly.
A good first step is making sure your signup page itself loads cleanly for a new visitor. The free check at funnelsitter.com/check loads one page of your app and reports its HTTP status, console errors, broken links, unfinished buttons and load time, with a screenshot, no account needed. It doesn't test the email step itself, so keep doing the fresh-address signup test above as well.