Securealy
Guide · Email · Authentication

Why do my magic-link emails land in spam?

Because the domain the email sends from is not authenticated, or is not yours. Receivers check SPF, DKIM and DMARC on the sending domain, and a sign-in email from a domain without them, or from your auth provider's shared default sender, is treated as suspect. Fix the sending domain, not the template.

Which domain is actually sending?

The one in the From address of the email, which is set by your auth provider or email service, not by your website. With Supabase Auth and no custom SMTP it is a supabase.io address you cannot authenticate. With Resend or similar it is whichever domain you verified there. Read a real message header to be sure.

Supabase documents the default plainly: if you use the built-in provider, the email domain is supabase.io, it is for demonstration purposes, and the limit is 2 messages per hour, a number that can change without notice. Two sign-ups in an hour and the third person waits. Nothing you add to your own DNS helps, because the mail is not from your domain. Custom SMTP from a domain you control is the only route to production.

Once custom SMTP is set, the sending domain is the one you verified at the email service. It may be a subdomain such as mail.yourdomain.com, and the records then belong on that subdomain. The grade works this out the way a receiver does: auth-sender-authenticated reads the DKIM selectors on your domain to find the provider, assumes the provider's shared sender when a Supabase-backed app has none, and judges the domain it finds. Your own domain with all three records passes; the shared sender, or your domain missing SPF or DMARC, fails.

What records does the sending domain need?

Three. One SPF TXT record at the domain naming the service allowed to send as you. One DKIM TXT record at the selector your provider gives you, so receivers can verify the signature. One DMARC TXT record at _dmarc with a policy and a reporting address. Google requires SPF or DKIM from every sender and DMARC from bulk senders.

Google's requirements, in force since February 2024, say every sender must set up SPF or DKIM email authentication for your sending domains, and senders of 5,000 or more messages a day must have both plus a DMARC policy. Yahoo's best practices ask the same. A domain that sends only sign-in codes is under the bulk threshold, but it is also new, unknown and sending links, which is what a phishing domain looks like; all three records are how it stops looking like one.

With Resend as the example provider, adding a domain shows the DKIM and SPF records to copy, and the DMARC guide recommends starting with p=none before moving to a stricter policy. The set, with host names only (your DNS provider appends the zone):

TXT  send               v=spf1 include:amazonses.com ~all        # or the value your provider shows
MX   send               feedback-smtp.REGION.amazonses.com  10
TXT  resend._domainkey  p=COPY_THE_WHOLE_VALUE_FROM_THE_PROVIDER
TXT  _dmarc             v=DMARC1; p=quarantine; rua=mailto:dmarc@YOUR_DOMAIN; pct=100

Two rules. There can be only one SPF record on a name: a second one added for a new provider makes receivers stop evaluating, so two records equal none. Merge the includes into one record. And paste the DKIM value whole; one character off and DKIM silently fails while mail still sends, straight to spam. The checks are spf, dkim and dmarc, and each page has the dig line that proves the record.

Does DMARC matter if I only send sign-in codes?

Yes. DMARC is what receivers check first, and p=none observes without protecting. A policy of quarantine or reject with a rua address stops anyone sending as you, keeps your own mail from being treated as suspect, and sends you the daily reports that show whether a legitimate sender is failing alignment.

DMARC ties the other two together. It tells a receiver what to do when SPF and DKIM do not align with the From domain, and where to send the aggregate reports. Without it, your domain can be spoofed by anyone and receivers treat mail from it with suspicion, including the sign-in codes and receipts the product depends on. dmarc passes a record with v=DMARC1, a policy of quarantine or reject and a rua=mailto: address; p=none or no reporting address warns; no record fails.

The reporting address is not decoration. Make dmarc@YOUR_DOMAIN forward to an inbox you read; the reports are the evidence that every sender aligns, and the kit asks for 30 clean days before moving to p=reject. Start at p=quarantine if your email service is the only sender, or at p=none for 14 days first when other systems already send as the root domain, so you learn about them from the reports rather than from bounced invoices.

How do I wire Supabase Auth to my own domain?

Create a sending-only API key at your email service, then set every SMTP field in Supabase Auth together: host, port, username, password, sender name and sender address at your domain. A partial write empties the rest and the project falls back to the shared sender silently. Then send a real reset and read its headers.

In Supabase, Authentication, SMTP Settings: host smtp.resend.com, port 465, username resend, password the sending-only key, sender name your product, sender email login@YOUR_DOMAIN. Set all six in one save. The Module 08 notes in the Fix Kit record what happens otherwise: a password rotation that sent only the password field was treated as the whole block, host, port, user and sender came back empty, and the product fell back to the shared, rate-limited mailer without an error. If you change SMTP through the management API, send every smtp_* field from a fresh read.

While you are in the templates, show the six-digit {{ .Token }} and drop {{ .ConfirmationURL }}. Corporate mail scanners open one-time links before the user does and spend them; a code survives the scan. Then prove the whole path from a terminal:

dig +short TXT resend._domainkey.YOUR_DOMAIN      # a line starting p=
dig +short TXT YOUR_DOMAIN | grep -c spf1         # 1
dig +short TXT _dmarc.YOUR_DOMAIN                  # v=DMARC1; p=quarantine; ...
curl -s -X POST "SUPABASE_URL/auth/v1/recover" -H "apikey: PUBLISHABLE_KEY" \
  -H "Content-Type: application/json" -d '{"email":"YOUR_EMAIL"}'   # {}

The message must arrive from login@YOUR_DOMAIN with SPF: PASS, DKIM: PASS and DMARC: PASS in its headers. A message from a supabase.io address means the SMTP block is incomplete. The full runbook is in Fix Kit Module 08; the free version is on the check page.

Grade it

See which domain your sign-in mail comes from

The grade finds the sending domain from your DNS and judges it the way an inbox does. Four email checks, each with the record to add. Two free grades a day.

Checks this guide refers to: auth-sender-authenticated, spf, dkim, dmarc, mx, supabase-auth-config.