Deliverability

How to Improve Email Deliverability: SPF, DKIM, DMARC and the Gmail/Yahoo Rules

Delivered is not the same as inboxed. A record-level guide to SPF, DKIM and DMARC, custom MAIL FROM on Amazon SES, the Gmail and Yahoo bulk sender rules, and how to confirm it all on a real message.

By Sangeeth · June 2026 · 16 min read

How to Improve Email Deliverability: SPF, DKIM, DMARC and the Gmail/Yahoo Rules

Deliverability problems rarely announce themselves. Your platform reports a campaign as "delivered" because the receiving server accepted it, and the mail then sits in spam where nobody sees it.

To improve email deliverability you fix two separate things. Identity: receivers must be able to prove mail from your domain really is from you, which is what SPF, DKIM and DMARC do. Reputation: once receivers know who you are, they judge whether recipients want your mail. This guide covers the record-level detail, the Gmail and Yahoo bulk sender rules, and how to verify everything on a real message.

If you are diagnosing a spam-folder problem right now, why emails go to spam works from symptoms to causes. This article is the configuration reference.

The three domains every message carries

Most authentication confusion comes from not realising that one email involves up to three different domains, and each protocol checks a different one.

IdentifierWhere it appearsChecked byExample
Header FromThe From line the recipient seesDMARC (as the domain to align with)news@example.com
MAIL FROM (envelope sender, Return-Path)SMTP envelope; bounces go hereSPFbounce.example.com
DKIM signing domainThe d= tag in the DKIM-Signature headerDKIMexample.com

SPF can pass for the envelope domain while the visible From says something else, and DKIM can pass for your provider's domain rather than yours. DMARC ties them together: it passes only when SPF or DKIM passes for a domain that aligns with the header From.

SPF: one record, ten lookups, and the right domain

SPF (RFC 7208) lets a domain publish which servers may use it as the envelope sender. The receiver takes the MAIL FROM domain, fetches its SPF record, and checks whether the connecting IP is authorised.

example.com.  TXT  "v=spf1 include:_spf.google.com include:spf.crm-example.net ~all"

Each include: tells the receiver to evaluate another domain's SPF record. The final ~all says what to do with any IP that matched nothing.

Only one SPF record per domain

RFC 7208 says a domain must not publish multiple SPF records; if a receiver finds more than one, the result is permerror and SPF is broken for every sender on the domain. It usually happens when a new tool's setup page says "add this TXT record" and someone adds a second v=spf1 record instead of merging the mechanisms into the existing one.

The 10-lookup limit, including nested includes

RFC 7208 section 4.6.4 limits SPF evaluation to 10 terms that require DNS lookups: include, a, mx, ptr and exists mechanisms plus the redirect modifier. Exceeding it returns permerror. The count is recursive: each include counts, and so does every lookup inside the record it points to.

The RFC also says implementations should limit "void lookups", queries that return no records, to two. An include pointing at a domain that no longer publishes SPF (a cancelled tool, a typo) can push you into permerror even when your lookup count is low.

Counting lookups by hand

example.com     v=spf1 include:_spf.google.com include:spf.crm-example.net mx ~all
  include:_spf.google.com     1 lookup
    (+ every include inside Google's record)
  include:spf.crm-example.net 1 lookup
    include:spf1.crm-example.net   +1
    include:spf2.crm-example.net   +1
  mx                          1 lookup
                              ----------------
                              4 + whatever Google nests

Resolve the chain with dig +short TXT on each included domain rather than trusting an old count; providers change their own records. The cleanest fix for a crowded root SPF record is usually not "flattening" it into IP addresses, which goes stale silently when a provider changes ranges. It is moving bulk mail onto its own envelope domain, covered in the custom MAIL FROM section below, so the bulk provider's include lives in a separate record with its own lookup budget.

~all vs -all

In RFC 7208, -all produces fail, an explicit statement that unlisted hosts are not authorised, while ~all produces softfail, a weaker statement that they are probably not authorised. The RFC advises receivers not to reject on softfail alone.

Once DMARC is enforced, the difference matters less than people think, because DMARC decides the outcome. Use ~all while you are still discovering senders and move to -all when DMARC reports show no legitimate source failing SPF. Never publish +all, and avoid the ptr mechanism, which RFC 7208 says should not be published.

DKIM: selectors, key length and CNAME delegation

DKIM (RFC 6376) has the sending server sign selected headers and the body with a private key. The receiver reads the d= (domain) and s= (selector) tags from the DKIM-Signature header and fetches the public key from selector._domainkey.domain.

DKIM-Signature: v=1; a=rsa-sha256; d=example.com; s=s2026a;
    h=from:to:subject:date:message-id:list-unsubscribe:list-unsubscribe-post;
    bh=...; b=...

The receiver looks up s2026a._domainkey.example.com. If the key is missing, malformed or does not match, DKIM fails.

Selectors let you run several keys at once

Your workspace mail, email platform and helpdesk can each sign with their own key under different selectors on the same domain without interfering. Selectors are also how you rotate keys without downtime: publish the new key under a new selector, switch signing to it, and remove the old record only after mail signed with it has stopped arriving.

Use 2048-bit keys

Use 2048-bit RSA keys where your DNS provider supports them. Amazon SES Easy DKIM defaults to 2048 bits and offers 1024 as a fallback for DNS providers that cannot handle longer records (AWS documentation). A 2048-bit key exceeds the 255-character limit of a single TXT string, so it is stored as several quoted strings in one record; a dashboard that splits it wrongly corrupts the key.

CNAME delegation: how Amazon SES Easy DKIM works

Instead of pasting a public key into your DNS, many providers ask you to publish CNAME records that point to keys they host. With Amazon SES Easy DKIM you publish three CNAME records, and the value of each is the DKIM token followed by an SES hosted zone (AWS documentation):

tok1abc._domainkey.example.com.  CNAME  tok1abc.dkim.amazonses.com.
tok2def._domainkey.example.com.  CNAME  tok2def.dkim.amazonses.com.
tok3ghi._domainkey.example.com.  CNAME  tok3ghi.dkim.amazonses.com.

Copy the exact names and targets from the console: the tokens are unique to your identity, and AWS notes the hosted zone part of the target varies by Region. Delegation lets the provider manage key material without further DNS edits, but the CNAMEs must stay in place as long as you send through that provider. AWS's troubleshooting notes list the usual failures: dashboards that silently append your domain to the host field, an extra leading underscore, and providers that reject underscores. SES can take up to 72 hours to detect the records.

Custom MAIL FROM on Amazon SES

By default, Amazon SES uses a subdomain of amazonses.com as the MAIL FROM domain. SPF passes, but for amazonses.com, which does not align with your From domain, so SPF contributes nothing to DMARC. Your DMARC result then rests entirely on DKIM.

A custom MAIL FROM domain fixes that. You choose a subdomain you own and publish two records on it (AWS documentation):

bounce.example.com.  MX   10 feedback-smtp.us-east-1.amazonses.com.
bounce.example.com.  TXT  "v=spf1 include:amazonses.com ~all"

The Region in the MX target must match the SES Region you send from. Under relaxed DMARC alignment, SPF for bounce.example.com aligns with a From address at example.com, so you now have two aligned passes and DKIM is no longer a single point of failure.

A few details from the AWS documentation that cause real setup failures:

  • The MAIL FROM subdomain should not be one you send from or receive mail on. Use a dedicated name such as bounce or mail.
  • It must have exactly one MX record. More than one and the setup fails.
  • "Behavior on MX failure" decides what happens if the MX record disappears later. "Use default MAIL FROM domain" silently falls back to amazonses.com (sending continues, SPF alignment is lost). "Reject message" stops sending with a MailFromDomainNotVerified error. Choose deliberately and monitor either way.

With a custom MAIL FROM, the SES include belongs on bounce.example.com, not your root domain, whose SPF record then only needs services that use the root as their envelope domain.

When you connect a domain to SendDoggie, it gives you the exact DKIM, SPF and DMARC records to publish. Its guided domain authentication detects which DNS provider hosts your domain and live-checks each DKIM, SPF and DMARC record as you add it, which catches the appended-domain and wrong-host mistakes before you send anything.

DMARC: alignment, rollout and reports

DMARC is published as a TXT record at _dmarc.example.com. It tells receivers how to treat mail that fails alignment and where to send reports.

Relaxed and strict alignment

Under RFC 7489, alignment is relaxed by default for both SPF (aspf) and DKIM (adkim). Relaxed alignment requires the authenticated domain and the From domain to share an organisational domain, so bounce.example.com aligns with example.com. Strict alignment (aspf=s, adkim=s) requires an exact match. Strict mode breaks the custom MAIL FROM pattern above and rarely adds real protection; leave alignment relaxed unless you have a specific reason.

A message passes DMARC when at least one of SPF or DKIM passes for an aligned domain. It does not need both.

Roll out from none to quarantine to reject

; Stage 1: monitor
_dmarc.example.com.  TXT  "v=DMARC1; p=none; rua=mailto:dmarc@example.com"

; Stage 2: quarantine failing mail
_dmarc.example.com.  TXT  "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"

; Stage 3: reject failing mail
_dmarc.example.com.  TXT  "v=DMARC1; p=reject; rua=mailto:dmarc@example.com"

Stay at p=none until aggregate reports show every legitimate source (invoicing, CRM, helpdesk, contact forms, not only marketing) passing aligned SPF or DKIM. A billing tool that sends monthly needs at least one billing cycle in the reports. Then move to quarantine, watch again, and move to reject.

The sp= tag sets a separate policy for subdomains. If it is absent, subdomains inherit p=.

pct, and what replaced it

RFC 7489 defined pct= to apply the policy to a percentage of failing mail for a gradual rollout, with a default of 100. Many guides still recommend steps such as p=quarantine; pct=25. In May 2026 DMARC was republished as RFC 9989, which obsoletes RFC 7489, removes pct, and adds a t= test-mode tag: t=y asks receivers not to apply the published policy while reports continue (RFC 9989). Receivers built for the older spec will keep encountering both styles for some time, so do not build a rollout plan that depends on precise percentage sampling. Stage the rollout by policy level and, if needed, by subdomain.

Reading rua reports

Aggregate (rua) reports are XML files listing source IPs, message counts and authentication results. They are how you find senders you did not know about; send them to a dedicated mailbox or a report-processing service.

If the rua address is on a different domain, such as a reporting vendor, RFC 7489 requires that domain to authorise receiving reports for you, or receivers ignore the address:

example.com._report._dmarc.reports-vendor.net.  TXT  "v=DMARC1"

Gmail and Yahoo bulk sender requirements

Since February 2024, Gmail and Yahoo have enforced published requirements for bulk senders. These are a floor, not a guarantee of inbox placement.

RequirementGmailYahoo
Who counts as bulkClose to 5,000 or more messages to personal Gmail accounts in 24 hours, counted across the primary domain; the status does not expireBulk senders (no fixed number stated on the page)
SPF and DKIMBoth, for bulk senders (SPF or DKIM for everyone)Both
DMARCRequired; p=none is acceptableRequired; minimum p=none, and DMARC must pass
AlignmentFrom domain aligned with SPF or DKIM domainFrom domain aligned with SPF or DKIM; relaxed is acceptable
UnsubscribeOne-click (RFC 8058) for marketing and subscribed mail; honour within 48 hoursOne-click, RFC 8058 POST highly recommended; visible link in body; honour within 2 days
Spam rateBelow 0.3% in Postmaster Tools; aim below 0.1%Below 0.3%
InfrastructureValid forward and reverse DNS, TLS, RFC 5322 formattingValid forward and reverse DNS, RFC 5321 and 5322 compliance

Sources: Google sender guidelines, Google sender guidelines FAQ, Yahoo Sender Hub.

Three details are easy to miss. Google counts volume across the primary domain, so splitting mail across subdomains does not keep you under the threshold. A bulk sender above 0.3% is not eligible for mitigation until the rate has stayed below 0.3% for seven consecutive days. And Yahoo calculates complaint rate only from mail delivered to the inbox, so a sender already landing in spam can look healthier than it is.

Google's FAQ also says transactional messages are excluded from the one-click requirement, and that from November 2025 Gmail is ramping up enforcement on non-compliant traffic.

What one-click unsubscribe actually requires

RFC 8058 needs two headers, and both must be covered by a valid DKIM signature (listed in its h= tag), or receivers should not offer the one-click option:

List-Unsubscribe: <https://example.com/u/8f3a91c2>, <mailto:unsub@example.com?subject=8f3a91c2>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

The List-Unsubscribe header must contain an HTTPS URI. When the user clicks unsubscribe in their mail client, the receiver sends a POST to that URI with List-Unsubscribe=One-Click as the body. Your endpoint must process it without cookies or login, and must not respond with a redirect or a "are you sure?" page. A common bug is an endpoint that only handles GET, so the receiver's POST fails and the unsubscribe silently does nothing. Test with a real POST:

curl -i -X POST -d "List-Unsubscribe=One-Click" https://example.com/u/8f3a91c2

Expect a 2xx response, and confirm the contact is suppressed in your platform afterwards.

Check a received message's Authentication-Results header

DNS lookups tell you what you published. The Authentication-Results header tells you what a receiver concluded about an actual message, which is the only result that matters. Send a campaign to a Gmail address, open it, choose "Show original" from the message menu, and find the header near the top. Other clients have a "view source" or "show original" equivalent.

Authentication-Results: mx.google.com;
       dkim=pass header.i=@example.com header.s=tok1abc header.b=Qx7f...;
       dkim=pass header.i=@amazonses.com header.s=ug7n... header.b=Lm2p...;
       spf=pass (google.com: domain of 0100019a...@bounce.example.com
           designates 203.0.113.25 as permitted sender)
           smtp.mailfrom=0100019a...@bounce.example.com;
       dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=example.com

Read it in this order:

  1. header.from: the domain DMARC evaluated. Confirm it is the domain you think you send from.
  2. dkim=pass header.i / header.d: there may be more than one signature. At least one must be from your domain (or a subdomain of it). A pass only for your provider's domain does not align.
  3. smtp.mailfrom: the envelope domain SPF checked. If it is the provider's domain, SPF is not aligned, and DMARC rests on DKIM alone.
  4. dmarc=pass, and the policy Gmail read (p=). If the policy shown is not what you published, your DNS change has not propagated or you edited the wrong record.

Results like spf=permerror point to multiple records or the lookup limit; dkim=neutral (body hash did not verify) usually means something modified the message after signing, such as a gateway adding a footer.

Use Google Postmaster Tools

Google Postmaster Tools is the only first-party view of how Gmail treats your mail. It reports on mail sent to personal Gmail accounts, with dashboards for spam rate, reputation, authentication and delivery errors (Google Postmaster Tools help). You verify your sending domain with a TXT or CNAME record; until the domain is verified, no data appears.

The spam rate here is the figure Gmail's 0.3% threshold refers to, so check it after every large send. Days with too little volume show no data, so an empty dashboard for a small sender is not necessarily a misconfiguration. Google's FAQ also points bulk senders to a compliance status view in Postmaster Tools.

Warming up a new domain

A new domain has no reputation, and receivers treat a sudden burst of mail from an unknown domain with suspicion. Warming up means building history gradually with recipients likely to engage.

  1. Authenticate before the first send, and confirm it on a real message's Authentication-Results.
  2. Start with your most engaged recipients: people who opened, clicked or bought recently. They produce positive signals and few complaints.
  3. Widen the audience in steps over several weeks, moving on only while complaints and bounces stay low, and keep a steady cadence rather than occasional blasts.
  4. Watch Postmaster Tools and bounce logs after each step, and hold or step back if spam rate or deferrals rise.

Do not switch to a fresh domain to escape a damaged one. It starts with no history, and the behaviour that hurt the old domain will hurt the new one.

List hygiene is part of deliverability

Authentication proves who you are; your list decides what your mail looks like to receivers. Suppress hard bounces after the first occurrence, use confirmed opt-in on forms that attract bots, re-engage long-inactive contacts and then stop mailing those who do not respond, and never send to purchased lists. Our guide to cleaning your email list covers the process step by step.

BIMI, briefly

BIMI displays your logo next to messages in supporting mail clients. It does not improve deliverability by itself; it is a reward for having strong authentication. Gmail requires a DMARC policy of quarantine or reject (not none), a Verified Mark Certificate or Common Mark Certificate, and an SVG logo in the SVG Tiny PS profile (Google Workspace help). Finish the DMARC rollout first and treat BIMI as optional.

Common mistakes

  • Adding your bulk provider's include to the root SPF record when the provider uses its own or a custom MAIL FROM domain. It burns lookups and authorises nothing.
  • Publishing p=reject on day one, before discovering the invoicing tool or CRM that sends as your domain.
  • Setting p=none and never looking at reports. Monitoring that nobody reads is not monitoring.
  • Deleting DKIM CNAMEs during a DNS cleanup for a provider you still use, or removing an old selector while mail signed with it is still in flight.
  • Treating 5,000 a day as the line below which nothing matters. Gmail's requirements for all senders include authentication and a spam rate below 0.3%.

Troubleshooting

spf=permerror

Run dig +short TXT example.com. More than one v=spf1 line is the cause; otherwise count lookups through the include tree and check every include still resolves.

SPF passes but DMARC fails

Look at smtp.mailfrom in Authentication-Results. If it is your provider's domain, SPF is not aligned. Either set up a custom MAIL FROM, or make sure DKIM signs with your domain.

dkim=fail or no DKIM result for your domain

Query the selector from the header: dig +short CNAME tok1abc._domainkey.example.com for delegated keys, or dig +short TXT for a pasted key. No answer usually means the record was created as tok1abc._domainkey.example.com.example.com. A TXT answer split oddly or with stray spaces means the key was corrupted on paste.

SES custom MAIL FROM stuck in pending or failed

Run dig +short MX bounce.example.com. You need exactly one MX record pointing at the feedback-smtp host for the correct Region. SES tries for 72 hours and then marks the setup failed, after which you must restart it.

DMARC reports never arrive

Check the rua mailbox exists and accepts attachments, and if it is on another domain, check the _report._dmarc authorisation record. Allow a couple of days after publishing.

Everything passes but mail still goes to spam

Identity is fine; the problem is reputation or content. Check Postmaster Tools spam rate, recent list sources, bounce rates and whether you increased volume suddenly. Our spam diagnosis guide covers the reputation causes.

Best practices

  • One SPF record per domain, with lookups counted recursively and kept under 10.
  • Bulk mail on a custom MAIL FROM subdomain so SPF aligns and the root record stays small.
  • 2048-bit DKIM signed with your own domain, one selector per sending service.
  • DMARC with relaxed alignment and a working rua address, moved from none to quarantine to reject as reports allow.
  • RFC 8058 one-click unsubscribe with both headers DKIM-signed, plus a visible link, processed within two days.
  • Spam rate in Postmaster Tools checked after each large send, ideally under 0.1%.
  • New domains warmed with engaged recipients first.
  • Hard bounces suppressed immediately; inactive contacts re-engaged or removed.

Key takeaways

  • Improving email deliverability means fixing identity (SPF, DKIM, DMARC) and then protecting reputation (list quality, complaints, volume patterns). DMARC needs SPF or DKIM to pass for a domain aligned with the visible From.
  • SPF breaks with a second record or more than 10 recursive lookups. A custom MAIL FROM on SES keeps the bulk provider out of your root record and gives you aligned SPF.
  • Roll DMARC out from p=none to quarantine to reject based on what rua reports show. RFC 9989 has removed pct, so do not plan around percentage sampling.
  • Gmail and Yahoo require authentication, alignment, one-click unsubscribe and a spam rate below 0.3% from bulk senders; Google recommends staying under 0.1%.
  • Verify on a received message's Authentication-Results header, not only in DNS.
  • If you would rather not hand-check each record, you can create a free SendDoggie account and connect your domain with the guided setup.

Sources

  1. Google: Email sender guidelines
  2. Google: Email sender guidelines FAQ
  3. Yahoo Sender Hub: Sender best practices
  4. Google: Set up Postmaster Tools
  5. RFC 7208: Sender Policy Framework (SPF)
  6. RFC 6376: DomainKeys Identified Mail (DKIM)
  7. RFC 7489: DMARC (2015)
  8. RFC 9989: DMARC (May 2026, obsoletes RFC 7489)
  9. RFC 8058: One-Click Unsubscribe
  10. Amazon SES: Authenticating email with DKIM
  11. Amazon SES: Creating and verifying identities
  12. Amazon SES: Using a custom MAIL FROM domain
  13. Google Workspace: Set up BIMI