BusiRocket

Article

Supabase confirmation emails not arriving: the 2-per-hour limit and the custom SMTP fix

Published on

You tested signup a dozen times and the confirmation email always arrived. The app goes live, the first real users sign up, and the emails never come. Or they do for the first two people and then the app shows "email rate limit exceeded". Password resets go missing the same way.

Nothing is broken in your code. The project is still sending through the email service Supabase includes for testing, and that service is built to stop exactly here.

What the built-in email service does

Supabase's documentation is explicit about it:

  • It only delivers to addresses in your project's team. Anyone else is refused. That is why it worked every time you tested with your own address.
  • It sends 2 messages per hour, and Supabase says that number can change without notice.
  • It has no delivery or uptime guarantee and is meant for trying out Auth, testing templates and demos, not for production.

So an app on the default service can confirm its builder and nobody else.

The error messages that point here

Depending on the call and the version, the app or the Auth logs show one of these:

  • email_address_not_authorized: "Email sending is not allowed for this address as your project is using the default SMTP service." The user is not on your team.
  • over_email_send_rate_limit, often surfaced as "email rate limit exceeded": the hourly email limit is spent.
  • A 429 status on the signup or reset request: a rate limit was hit.

AI-built apps often swallow these errors and show a cheerful "check your inbox" anyway, so also look at the Auth logs in the Supabase dashboard, not only at the screen.

The fix: send through your own SMTP provider

Supabase Auth works with any email service that speaks SMTP. Its docs name Resend, AWS SES, Postmark, Twilio SendGrid, ZeptoMail and Brevo. The steps are the same with any of them:

  1. Create an account with the provider and add your domain. The provider gives you DNS records (SPF, DKIM, sometimes a return-path CNAME) to add where your domain's DNS lives. Wait until the provider shows the domain as verified.
  2. Get the SMTP details: host, port, username and password (often an API key used as the password).
  3. Enter them in Supabase: Authentication settings, SMTP, enable custom SMTP. Set the sender to an address on the domain you just verified, such as no-reply@example.com, and a sender name users will recognise.
  4. Send a real test: sign up with an address outside your team, ideally on Gmail and on Outlook, and check that the email arrives and the link works.

Once custom SMTP is saved, Supabase sends to every address, but still applies a starting limit of 30 emails per hour. If you expect more signups than that, raise it on the Rate Limits page under Authentication. Supabase also waits 60 seconds before letting the same user request another confirmation, magic link or reset, so a user who taps "resend" twice will hit that and not the hourly limit.

Arrives, but in spam

Custom SMTP fixes delivery; landing in the inbox depends on the domain. The usual reasons a confirmation email goes to spam:

  • The sender is a Gmail or Outlook address, or a domain you have not verified with the provider. Send from your own domain.
  • SPF or DKIM records are missing or wrong. The provider's dashboard shows whether they pass. If the domain already sends mail from another service, merge the SPF records into one; two separate SPF records break both.
  • No DMARC record. Gmail and Yahoo expect one from bulk senders; a basic p=none record at _dmarc.example.com is the minimum.
  • The template still looks like a default. A clear subject, your product name and a single obvious link help more than you would expect.

Lovable Cloud projects

If the app runs on Lovable Cloud rather than on a Supabase project you own, the auth email settings are not in a Supabase dashboard you can open. Look in Lovable under the Cloud section, Users, and Lovable's documentation on sending branded emails from your own domain. The principle is the same: production email has to go out from your own, verified domain.

Turning off confirmation is not the fix

The quick workaround the AI may suggest is switching off "Confirm email" so users get in without the email. It makes the symptom go away, but anyone can then sign up with any address, including someone else's, and password resets still need email. Use it while testing if you must, not in production.

When the email arrives but the link goes nowhere

If the email now arrives and the link sends users to localhost:3000 or to the preview, that is a different setting: see Supabase login redirects to localhost after deploy. And if users confirm, log in and then hit permission errors on their first save, the next stop is the "new row violates row-level security policy" error.

Email is one of several things that only fail once real users arrive; the others are in why an app works in preview but not in production. If you would rather have someone check the whole path from signup to payment before launch, that is our vibe code rescue audit: 450 EUR, 48 working hours, a written report of what is broken and what each fix costs, with the fee deducted if you continue into the rescue (1,500-6,000 EUR, fixed scope).

Next step

Does this problem sound like yours?

Tell us about it and we will say how we would approach it, in what order and at what cost.