Supabase Email Auth: Why Is My Verification Email Not Arriving?
1. "I Signed Up, But Where's the Email?"
On launch day, I asked my friends, "Hey, sign up for my app!" A minute later, I got a text. "I clicked sign up, but the email isn't coming."
Testing it myself, nothing arrived.
Checked the Spam folder. Nothing.
Checked Supabase Dashboard logs. It said Auth: Email sent.
"It says sent, but where did it go?"
In that moment, I realized sending an email isn't just calling a simple sendMail() function.
My precious verification email had been marked as 'Spam' somewhere in the vast ocean of the internet and evaporated.
2. What Confused Me Initially? (Betrayed by Supabase)
Since Supabase is a "Backend-as-a-Service," I assumed it would handle email delivery perfectly. The docs even said "Email Auth is enabled by default."
But I missed a crucial disclaimer:
"Supabase provides a default SMTP for testing purposes only. Rate limits apply."
Supabase's default email server is for "Testing Only." Tens of thousands of free tier users share this single server. If someone sends spam through it, the server's IP Reputation tanks.
Servers like Gmail, Outlook, and Yahoo look at emails from Supabase's default server with extreme suspicion. "Oh? This IP was sending gambling spam yesterday. Block."
My legitimate service emails were getting blocked simply by association.
3. The 'Aha!' Moment (The Mailman Analogy)
A friend explained it as "Public Mailbox vs. Private Courier."
- Supabase Default SMTP: A free public mailbox in the neighborhood. Since anyone can use it, some put trash or bombs in it. The Post Office (Gmail) treats mail from here as suspicious. It also has strict limits (like 3 emails per hour).
- Custom SMTP (Resend, AWS SES): A paid private courier in a suit. Identity verified (Domain Auth), dressed neatly (DKIM/SPF), so the Post Office waves them through via the priority lane.
"Ah, if I don't want my service to be treated like junk, I need to hire a private courier." I immediately started setting up Custom SMTP.
4. The Fix: Integrating Resend
AWS SES was too complex, so I chose Resend for its developer-friendly experience. (100 free emails/day is enough for verified domains initially).
Step 1: Domain Authentication (Crucial)
I signed up for Resend and added my domain (codemapo.com).
They gave me 3 DNS records:
- DKIM (DomainKeys Identified Mail): A digital signature proving "This email is really from codemapo.com."
- SPF (Sender Policy Framework): "Resend servers are authorized to send email on behalf of my domain."
I had to copy these into my DNS settings (Vercel, Cloudflare). If you skip this? It's like sending a courier in a suit but without an ID card. Still treated as spam.
Step 2: Supabase Integration
Go to Supabase Dashboard -> Project Settings -> Auth -> SMTP Settings.
Don't just toggle Resend integration; verify Custom SMTP settings.
- Sender Email:
noreply@codemapo.com(Must use your domain! Don't use@gmail.com) - Host:
smtp.resend.com - Port:
465(SSL) - User:
resend - Pass: (API Key from Resend)
Step 3: Test
After saving, I tried signing up again. Less than a second later, my phone buzzed. "Ding!" It landed proudly in the Inbox, not Spam. The satisfaction!
5. Deep Dive: The Holy Trinity of Email (SPF, DKIM, DMARC)
To stay out of the spam folder, you must understand these three.
-
SPF (Sender Policy Framework)
- Meaning: "List of employees with ID badges."
- Action: Receiver asks DNS, "This email claims to be from you, but came from IP
1.2.3.4. Is this your guy?" DNS repliesv=spf1 include:resend.com, meaning "Yes, Resend is our authorized sender."
-
DKIM (DomainKeys Identified Mail)
- Meaning: "Wax Seal on the envelope."
- Action: Attaches an encrypted signature to the header. Receiver decrypts it with the public key to verify, "Oh, the content wasn't tempered with during delivery."
-
DMARC
- Meaning: "What to do with fakes?" (Instructions)
- Action: Policy telling the receiver what to do if SPF or DKIM fails. "Just mark as spam (quarantine)" or "Reject it entirely (reject)." Start with
none(monitoring) and move toquarantinelater.
6. Deep Dive: Choosing the Right SMTP Provider
Which one should you pick in 2025?
| Provider | Free Tier | Pros | Best For |
|---|---|---|---|
| Resend | 3,000/mo | Amazing DX, React Email support | Startups / Indy Devs |
| AWS SES | 62,000/mo | Dirt cheap ($0.10/1k emails) | Scale / DevOps Pros |
| SendGrid | 100/day | Reliable, feature-rich | Legacy systems |
| Mailgun | Trial Only | Strict enforcement | Not recommended |
I vote for Resend. It's built by the same aesthetic philosophy as Vercel. Their React Email library allows you to build email templates using React components instead of archaic HTML <table> tags.
7. Deep Dive: Domain Warm-up
Just buying a domain and setting up SMTP isn't enough.
If you buy newapp.com today and send 10k emails tomorrow, you WILL be blocked.
The Warm-up Schedule:
- Week 1: 50 emails/day
- Week 2: 100 emails/day
- Week 3: 500 emails/day
This builds "IP Reputation."
Also, keep an eye on Bounce Rate. If you send emails to non-existent addresses (e.g., test@test.com), your reputation score drops. Use an email validation library on your signup form.
8. Tip: The Localhost Trap
Be careful when testing locally (localhost:3000).
When you click the Confirm your email link, Supabase redirects the user to the Site URL.
If Site URL is set to production (https://codemapo.com), signing up locally will redirect you to the live site after verification, making it look like login failed (session is on prod, not localhost).
Solution:
Add http://localhost:3000/** to Redirect URLs in Supabase Auth settings.
And explicitly specify emailRedirectTo in your client code:
await supabase.auth.signUp({
email,
password,
options: {
emailRedirectTo: 'http://localhost:3000/auth/callback', // Explicitly set local URL
},
});
9. One-Line Summary
Supabase default email is a toy. If you're launching a real service, integrate a Custom SMTP like Resend from Day 1 and set up SPF/DKIM 'ID cards'.