Your password-reset emails land in spam, or never arrive, and nothing in your application logs tells you why. Start by listing every service that sends mail as your domain: your transactional provider, your newsletter tool, your support desk, your CRM and Google Workspace. That list is the input for the whole SPF, DKIM and DMARC setup below.
The records themselves are quick to publish. The slow part is knowing which sender is misaligned or over a limit, because mailbox providers reject or filter silently. This guide covers the setup, the limits that break it, how to split sending streams, and how to compare providers, with the requirements checked against Google, Yahoo and the RFCs.
How do SPF, DKIM and DMARC work together?
SPF lists which servers may send mail for your domain, DKIM attaches a cryptographic signature that receivers verify through DNS, and DMARC tells receivers what to do when those checks fail to align with the From domain.
Each record does a different job:
- SPF publishes the authorized senders in a DNS TXT record. RFC 7208 defines how receivers evaluate it.
- DKIM signs each message. The public key lives in DNS under a selector, at `selector._domainkey.yourdomain.com`, as described in RFC 6376.
- DMARC sits on top and sets a policy of `none`, `quarantine` or `reject`, plus a reporting address. The current specification, RFC 9989, obsoletes the original RFC 7489 and keeps those three policy values.
Whether you need all three depends on your volume, and the big mailbox providers have spelled it out. Google's sender guidelines apply from February 1, 2024, and Yahoo's sender requirements cover the same ground:
| Requirement | Yahoo | |
|---|---|---|
| Who counts as bulk | About 5,000+ messages a day to personal Gmail accounts, and once you qualify you stay a bulk sender | Bulk senders, as defined on Yahoo's page |
| Authentication | SPF and DKIM, plus a DMARC setup | SPF and DKIM, plus a valid DMARC policy of at least `p=none` |
| Alignment | From domain aligned with the SPF domain or the DKIM domain | Not spelled out on the page I checked |
| Marketing mail | One-click unsubscribe | List-unsubscribe header with one-click support, honored within 2 days |
| Spam rate | Keep under 0.10% and avoid reaching 0.30% | Keep under 0.3% |
Google also says it began ramping up enforcement in November 2025, with messages that fail the requirements facing temporary and permanent rejections, and with bulk senders getting error codes first. That matters for a SaaS because a password reset is exactly the message you can't afford to lose.
Many domains still aren't there. The EasyDMARC 2026 DMARC Adoption Report, which analyzed 1.8 million top domains, found 52.1% with a DMARC record in 2026, up from 47.7% in 2025, and only around 9% combining an enforcement policy with reporting.
What DMARC policy should you start with?
Start at `p=none` with a reporting address, read the aggregate reports until every legitimate sender shows as aligned, and only then tighten to `quarantine` and `reject`.
A starting record for a sending subdomain looks like this (replace the placeholders):
``` _dmarc.mail.example.com. TXT "v=DMARC1; p=none; rua=mailto:dmarc-reports@example.com" ```
The `rua` tag, introduced in the original RFC 7489, names where aggregate reports go. Without it, no reports are generated, and you'd be tightening blind. I'd wait at least two weeks of reports before moving off `none`, which is my rule of thumb and not a standard. Jumping straight to `reject` with a misaligned DKIM selector means receivers reject your own password resets and your application sees nothing.
One change to watch: RFC 9989 replaces the old `pct` percentage tag with a `t` test-mode tag and adds `sp` and `np` tags for subdomain and non-existent-subdomain policy. Check what your DMARC reporting tool and DNS host support before relying on the newer tags.
Why does the SPF 10-lookup limit break email?
RFC 7208 requires receivers to cap DNS-querying terms at 10 during SPF evaluation and to return a permerror when the limit is exceeded, so an over-long record fails authentication even if every include is legitimate.
The terms that count are `include`, `a`, `mx`, `ptr`, `exists` and the `redirect` modifier. The RFC also recommends limiting void lookups, which are queries that return nothing, to two. Every service you add with an `include:` spends part of the same budget, and nested includes spend it too. A typical stack of a transactional provider, a marketing tool, Google Workspace, a help desk and a CRM can get there without anyone noticing, because nothing in a DNS panel warns you.
A practical order of work:
- Count your lookups with a checker such as MXToolbox's SPF tool, and delete includes for services you no longer use.
- Give each sending stream its own subdomain. Each subdomain has its own SPF record and its own 10-lookup budget.
- If you must stay under one domain, consider flattening includes into IP ranges. This is my inference from how the mechanism works: providers can change the ranges behind an include, so a static flattened record can go stale, and a flattening service that refreshes it is safer than a hand-maintained one.
The DKIM side fails differently. If the selector published in DNS doesn't match the one your provider actually signs with, verification fails and DMARC has nothing aligned to lean on. ### What should you check after adding a new sending service?
Treat every new provider as a small change request with its own checklist:
- Publish the records the provider gives you, then wait for its dashboard to show the domain as verified.
- Send a test message to a mailbox you control and read the authentication results in the raw headers. You want SPF, DKIM and DMARC all passing, with the From domain aligned to at least one of SPF or DKIM.
- Recount your SPF lookups, because the new include spent some of the budget.
- Watch the next batch of DMARC aggregate reports for that sender before you tighten any policy.
Skipping step two is how a misconfigured selector runs for weeks unnoticed.
Should transactional and marketing email use separate subdomains?
Yes. Separate subdomains keep a complaint-heavy newsletter from dragging down password resets and receipts, and they give each stream its own SPF budget, DKIM selector and DMARC policy.
Resend's domain documentation recommends sending from one or more subdomains instead of the root domain to isolate sending reputation. One workable layout:
| Subdomain | Use | Example provider type |
|---|---|---|
| `mail.yourdomain.com` | Password resets, receipts, booking confirmations | Transactional provider |
| `marketing.yourdomain.com` | Campaigns, drip sequences, newsletters | Marketing platform |
| `reply.yourdomain.com` | Support mail in both directions | Help desk tool |
The transactional subdomain can tighten DMARC sooner, because its volume and senders are predictable. The marketing subdomain can stay at `quarantine` longer while you watch complaint rates in Google Postmaster Tools, whose dashboards show spam rate, reputation, authentication and delivery errors. Keep each subdomain's DNS records in version control or a short runbook, so whoever adds the next sender knows which lookup budget they're spending.
Two of my case studies show what transactional email looks like in production. BookBed, the Flutter, Firebase and Stripe booking platform, ships with eighteen-plus Resend email templates. Pizzeria Bestek, a React and Supabase ordering system, uses Supabase Edge Functions to send the customer confirmation and the admin email with its confirm and decline buttons, through a Resend account the restaurant owns after handover. In both, the order or booking email is the moment the product proves it worked.
Which transactional email service providers fit a SaaS?
Resend suits React and Next.js teams that want a generous free tier and React Email templates, while Postmark is a fit when a missed message is a real business problem. Check current pricing, because both change it.
The table uses each provider's own pricing page, read on 30 September 2026:
| Resend | Postmark | |
|---|---|---|
| Free tier | 3,000 emails a month, 100 a day, 3 domains | 100 emails a month |
| Entry paid plan | Pro, $20/month for 50,000 emails, 10 domains, no daily limit | Basic, $15/month for 10,000 emails |
| Dedicated IP | Not on Free or Pro, only on Scale plans for senders above 3,000 emails a day | From $50/month per IP, and requires 300,000+ emails a month |
| Stream separation | Use subdomains | Message streams, up to 15 on Basic |
For a Next.js and Supabase build, Resend is the natural default, and the Next.js and Resend integration page covers API-key scoping and the React Email workflow, while the SaaS MVP stack shows where email fits in the wider architecture. I'd move to Postmark, or add it for the critical streams, when a failed booking or payment email creates a support ticket on its own.
How do you warm up a new sending domain?
A new domain has no sending history, so start with low volumes to addresses that have confirmed they want your mail, and raise volume gradually while you watch your spam rate.
I'm not giving week-by-week volume targets, because I couldn't verify a standard schedule against a primary source. What the primary sources do give you is a ceiling: Google asks bulk senders to keep the spam rate under 0.10% and never reach 0.30%, and Yahoo sets 0.3%. Set up Postmaster Tools before your first real send so you can see that number. Validate any imported list first, and remove bounced and complained addresses promptly. A high bounce rate is a reputation risk, though that's my reading of how filters behave and I didn't find a published threshold to cite.
Count your SPF lookups today. If the total is over 10, which sending service would you move to its own subdomain first?
Part of the SaaS Backend Infrastructure: Jobs, Webhooks, Email, and Admin Tooling guide.



