Blazalek.com

03 / 10

Deliverability

The four pillars of reaching the inbox: SPF/DKIM/DMARC authentication, sender reputation, bounce & complaint handling, and DNS architecture that protects transactional mail.

By

Key takeaways

  • Reaching the inbox rests on four pillars: authentication (SPF/DKIM/DMARC), sender reputation, bounce & complaint handling, and DNS architecture.
  • Authentication is binary and cheap to fix; reputation is slow to build and the hardest to repair, fix authentication first.
  • Since 2024, Gmail and Yahoo require bulk senders to pass aligned authentication, offer one-click unsubscribe, and keep complaint rates low.
  • DMARC alignment, usually via DKIM signed with your own domain, is what ties an authentication pass back to your visible From address.
On this page

This chapter answers why your email lands in the inbox or the spam folder, and what to do about it. Deliverability is not one setting you flip on. It is the cumulative result of who is allowed to send for your domain, how trustworthy your sending history looks, and how you react when something goes wrong.

Mental model: mailbox providers (Gmail, Yahoo, Microsoft) treat every sender as guilty until proven trustworthy. Authentication proves your identity. Reputation proves your behavior. You need both.

This guide is provider-neutral. Everywhere a concrete provider would appear, the examples use a generic emailClient / provider abstraction or raw standards (DNS records, Node crypto, plain SMTP headers, the Standard Webhooks scheme). Real providers, Amazon SES, Postmark, SendGrid, Mailgun, Resend, Brevo, SparkPost, Mandrill, are named only as interchangeable examples in lists. Swap any of them in without changing the architecture in this chapter.

Quick answers to the questions covered here:

  • What are SPF, DKIM, and DMARC, and why are they now mandatory?
  • How do I read each DNS record and avoid the common setup mistakes?
  • How do I roll out DMARC without breaking my legitimate email?
  • How do I verify my setup with dig and test tools?
  • What is sender reputation and how do I warm a new IP or domain?
  • What bounce rate and complaint rate are too high?
  • How should I structure subdomains and DNS TTL?
  • What are BIMI, ARC, MTA-STS, and TLS-RPT, and do I need them?
  • What exactly do the Gmail/Yahoo/Microsoft 2024 bulk-sender rules require?
  • My email is going to spam: what do I check first?

Why does my email go to spam instead of the inbox?

Because something in one of four pillars is failing. Each pillar decides whether your mail gets delivered:

  1. Authentication: proving to receiving servers that the mail genuinely comes from you (SPF, DKIM, DMARC).
  2. Sender reputation: the long-term trust score mailbox providers attach to your domain and IP.
  3. Bounce and complaint handling: reacting correctly to failures and angry recipients before they poison your reputation.
  4. Infrastructure: the DNS and domain architecture that keeps marketing problems from sinking transactional mail.

Fix them roughly in this order. Authentication first, because nothing downstream matters if your mail cannot prove who sent it.

The journey of one message, end to end

  1. 1

    Your app

    calls your email provider's API (or hands off to an MTA)

  2. 2

    Provider MTA → recipient server

    connects over SMTP, ideally over TLS

  3. 3

    Connection-stage checks

    Is the IP on a blocklist? Does SPF pass?

  4. 4

    Content-stage checks

    Does DKIM verify? Does DMARC pass and align?

  5. 5

    Reputation

    domain & IP reputation, recipient engagement, spam traps

Delivery decision

Inbox
Spam
Rejected
Each deliverability pillar maps to one of these stages. Authentication failures are decided at stages 3–4.

It helps to know what actually happens between your send() call and the inbox, because every pillar maps to a stage:

  1. Your application calls your email provider's API (or hands a message to your own MTA).
  2. The provider's outbound MTA connects to the recipient domain's MX host over SMTP, ideally over TLS. MTA-STS and DANE govern whether that TLS is enforced.
  3. The receiving MTA runs connection-time checks: is the connecting IP on a blocklist (RBL)? Does the MAIL FROM (envelope sender, also called the Return-Path) domain publish SPF, and does this IP pass it?
  4. The receiver accepts the DATA and runs content-time checks: does the DKIM signature verify? Does DMARC pass and align? If the message was forwarded, does an ARC chain vouch for an upstream pass?
  5. The receiver consults reputation: domain reputation, IP reputation, the recipient's own engagement history with you, and spam-trap hits.
  6. The receiver applies policy: inbox, spam folder, "Promotions"-style tab, or outright rejection (a bounce). Bulk-sender rules (one-click unsubscribe, complaint-rate ceilings) bite here.

Authentication failures are decided at steps 3–4 and are binary and cheap to fix. Reputation (step 5) is analog, slow-moving, and the hardest to repair. Almost every "we suddenly went to spam" incident is a step 3–4 regression (a DNS edit, an expired key, a new vendor) that you can find in minutes.

Why is email authentication now non-negotiable?

Because since February 2024, Gmail, Yahoo, and Microsoft enforce authentication for bulk senders. Unauthenticated emails will be rejected or spam-filtered.

An email that fails authentication is no longer "probably spam." It is increasingly treated as definitely unwanted, and is either bounced outright or routed to spam. Authentication is the foundation: nothing else in this chapter matters if your mail cannot prove who sent it.

There are three records, and they build on each other. SPF and DKIM each independently say "this server or this signature is authorized." DMARC ties them to your visible From: address and tells receivers what to do when neither passes.

A "bulk sender" in this context generally means roughly 5,000 or more messages per day to a single provider, but treat all three records as mandatory regardless of volume. Providers increasingly apply the same checks to small senders too.

How the three records relate

RecordRFCWhat it provesWhat it checksSurvives forwarding?DNS location
SPF7208The connecting IP is authorized for the envelope-sender (Return-Path) domainConnecting IP vs. published listNoTXT at the domain apex or subdomain
DKIM6376The message was signed by a key you control and was not alteredCryptographic signature vs. published public keyYesTXT at selector._domainkey.domain
DMARC7489SPF or DKIM passed and aligns with the visible From: domainAlignment + policy + reportingVia DKIM (and ARC)TXT at _dmarc.domain

The crucial word is alignment. SPF and DKIM each authenticate a domain, but not necessarily the domain your recipient sees in From:. DMARC is the only one of the three that ties authentication back to the visible From: header, which is the only identity a human reads. That is why DMARC is the policy layer and SPF/DKIM are its inputs.

What is SPF and why do I need it?

SPF (Sender Policy Framework, RFC 7208) is a published list of which servers can send email for your domain. The receiving server checks the sending IP against this list. If the IP is not authorized, SPF fails.

SPF authenticates the envelope sender (the SMTP MAIL FROM, surfaced as the Return-Path header), not the visible From: header. This distinction matters for alignment later: many providers use their own bounce domain as the Return-Path, so SPF passes for their domain, not yours, which is fine for SPF itself but means you must rely on DKIM for DMARC alignment.

Add this as a TXT record in your DNS. The example uses one provider's include; substitute your own provider's published include token:

; TXT record at yourdomain.com
yourdomain.com.   IN   TXT   "v=spf1 include:amazonses.com ~all"
  • Add the TXT record to DNS.
  • Use ~all (soft fail) as the recommended default.
"v=spf1 include:amazonses.com ~all"
v=spf1
Record version: always first.
include:amazonses.com
Authorizes your provider's servers. Swap in the token your provider publishes.
~all
Soft fail: everything else is suspect but not hard-rejected. The recommended default.
A TXT record published on your domain. Exactly one SPF record per domain.

How to read the record

  • v=spf1: declares this is an SPF version 1 record.
  • include:amazonses.com: delegates authorization to a provider's own SPF record. Each provider publishes its own include mechanism, for example include:amazonses.com, include:sendgrid.net, include:spf.mandrillapp.com, include:_spf.mx.cloudflare.net, include:spf.messagingengine.com. Use exactly the token your provider documents.
  • ~all: soft fail. Anything not matched above is suspicious but not hard-rejected. This is the recommended default. -all (hard fail) is stricter but risky during setup, because a single misconfiguration silently kills delivery.

SPF mechanisms and qualifiers reference

MechanismMeaning
ip4:198.51.100.0/24Authorize an IPv4 address or CIDR range directly
ip6:2001:db8::/32Authorize an IPv6 range
a / a:hostAuthorize the IPs in the domain's (or named host's) A/AAAA records
mxAuthorize the IPs of the domain's MX hosts
include:other.comRecursively include another domain's SPF policy
redirect=other.comReplace this policy entirely with another domain's (not a fallback)
exists:%{i}._spf.example.comMacro-based dynamic lookup (advanced)
Qualifier (prefix on all)Result when nothing matchesUse
+allPass everythingNever. This authorizes the entire internet to spoof you.
~allSoft failRecommended default during and after rollout.
-allHard failEnd state once DMARC enforcement and DKIM are proven.
?allNeutralEffectively no policy; avoid.

Practical cautions

  • The 10-lookup limit. SPF caps the number of DNS-querying mechanisms (include, a, mx, ptr, exists, redirect) at 10. Exceed it and SPF returns permerror, which DMARC treats as an SPF failure. Each vendor include: can itself expand to several nested lookups, so two or three vendors can silently blow the budget. Audit the expanded count, not just the literal includes.
  • One SPF record per domain. There is exactly one SPF TXT record per domain. Two SPF TXT records is a permerror and SPF fails entirely. If you use several senders, merge their includes into a single record.
  • SPF breaks on forwarding. When a mailbox forwards your message, the forwarding server's IP is not on your list, so SPF fails at the next hop. This is precisely why DKIM (which travels with the message) and DMARC (which can pass on DKIM alone) exist, and why ARC was later added to repair forwarded mail.
  • The void-lookup limit. Beyond the 10-lookup cap, RFC 7208 also limits "void" lookups (those returning no records or NXDOMAIN) to 2. A vendor that retires a hostname can break your SPF without you touching it.

Flattening to stay under the lookup limit

If you legitimately need more includes than the budget allows, you can "flatten": resolve each vendor's includes to literal ip4:/ip6: ranges and publish those instead. This trades maintainability for headroom, you must re-flatten whenever a vendor changes IPs. Use a managed SPF-flattening service or a scheduled job that re-resolves and diffs, and never flatten a vendor that rotates IPs frequently.

Common mistake

Publishing a second SPF record when you add a new vendor. Always edit the existing record and add the new include: to it instead. The symptom is intermittent SPF passes that mysteriously stop, because some resolvers concatenate multiple TXT strings and others do not.

What is DKIM and how is it different from SPF?

DKIM (DomainKeys Identified Mail, RFC 6376) is a cryptographic signature proving email authenticity. SPF authorizes an IP; DKIM proves the message itself was signed by a key you control and was not altered in transit.

Your email provider will give you a public-key TXT record to publish (or, with some providers, a CNAME that points at a key they host and rotate for you).

How it works: DKIM attaches a cryptographic signature to each message, generated with a private key your sending provider holds. The matching public key lives in your DNS as a TXT record at a selector. The receiver reads the DKIM-Signature header, fetches the public key named by the s= (selector) and d= (signing domain) tags, and verifies the signature. A pass confirms two things: the mail really came from a sender holding the key, and the signed parts of the message were not altered in transit.

Anatomy of a DKIM-Signature header

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yourdomain.com;
  s=s1; t=1700000000; bh=<body hash>;
  h=from:to:subject:date:message-id;
  b=<signature>
  • d=: the signing domain. For DMARC alignment, this must match (or be a parent of, in relaxed mode) the From: domain.
  • s=: the selector, which names the DNS record holding the public key: s1._domainkey.yourdomain.com.
  • a=: the algorithm, typically rsa-sha256. Ed25519 (ed25519-sha256) is defined but not yet universally verified, so providers publish RSA as the primary.
  • c=: canonicalization for header/body. relaxed/relaxed tolerates whitespace and folding changes introduced by relays; simple/simple is brittle. Prefer relaxed.
  • bh=: the body hash. If a forwarder appends a footer or a mailing list rewrites the body, bh no longer matches and DKIM breaks even though the cryptography is sound.
  • h=: the list of headers covered by the signature. Headers not in this list can be added by relays without breaking the signature. Notably, signing From is mandatory.

The published public-key record

; TXT record at s1._domainkey.yourdomain.com
s1._domainkey.yourdomain.com.  IN  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...<base64 public key>...IDAQAB"

Many providers instead have you publish a CNAME so they can rotate the underlying key without you editing DNS:

s1._domainkey.yourdomain.com.  IN  CNAME  s1.dkim.provider.example.

Practical notes

  • The record lives at a selector subdomain, for example s1._domainkey.yourdomain.com, where s1 is the selector your provider assigns. Different providers, and even different keys, use different selectors. Publish multiple selectors when you send from multiple providers.
  • DKIM survives forwarding, because the signature travels with the message. This makes it more robust than SPF, and it is the mechanism DMARC usually relies on for alignment.
  • Use a 2048-bit RSA key when your provider offers it. Older 1024-bit keys still work but are weaker and are being deprecated. Note that a 2048-bit public key may exceed the 255-character limit of a single DNS TXT string and must be split into multiple quoted strings within one record (most DNS UIs handle this automatically).
  • Rotate keys periodically and whenever a provider supports it. Keep the old selector published for at least a few days after rotation so in-flight and queued mail still verifies. Never expose the private key.
  • DKIM signs a snapshot of selected headers and the body. Any modification to a signed header or to the body (a mailing-list footer, an antivirus "scanned by" banner, content rewriting) invalidates the signature. This is the single most common cause of DKIM failing on otherwise-correct mail.

Common mistakes

  • Signing with a d= that does not align with From: (e.g. the provider's own domain). This passes DKIM but fails DMARC. Always sign with your own domain.
  • Publishing the record but never enabling signing in the provider, so messages go out unsigned.
  • Letting a one-time test selector expire while production mail still references it.

What is DMARC and what does "alignment" mean?

Standard version. This section describes DMARC per RFC 7489 (2015), still widely deployed and fully functional. In May 2026 its successor, DMARCbis (RFC 9989), was published; it tidies up and extends the standard. Your existing records keep working unchanged; what changes is covered in DMARCbis (RFC 9989): what the latest standard changes below.

DMARC (Domain-based Message Authentication, Reporting, and Conformance, RFC 7489) is the policy for handling SPF/DKIM failures, plus reporting. It requires alignment: the domain that passes SPF or DKIM must match the domain in the visible From: header.

DMARC passes if either:

  • SPF passes and the Return-Path domain aligns with the From: domain, or
  • DKIM passes and the d= signing domain aligns with the From: domain.

Only one needs to pass and align. This is why DKIM is the workhorse: it survives forwarding and aligns with your domain even when the provider owns the Return-Path.

Add this as a TXT record at _dmarc.yourdomain.com:

; TXT record at _dmarc.yourdomain.com
_dmarc.yourdomain.com.  IN  TXT  "v=DMARC1; p=none; rua=mailto:dmarc@yourdomain.com; fo=1"

Why it matters: SPF and DKIM authenticate the technical sender, but a spammer can still pass SPF/DKIM for their own domain while spoofing your brand in the visible From:. DMARC closes that gap by requiring alignment. It also asks receivers to report what they see, so you gain visibility into who is sending as you.

How to read the record (full tag reference)

TagExampleMeaning
vDMARC1Version. Required, must be first.
pnone / quarantine / rejectPolicy for the organizational domain.
sprejectPolicy for subdomains, overriding p. Set explicitly to stop subdomain spoofing.
ruamailto:dmarc@yourdomain.comWhere to send aggregate (XML) reports.
rufmailto:forensic@yourdomain.comWhere to send forensic/failure reports (often unsupported by receivers, and privacy-limited).
pct25Percentage of failing mail the policy applies to. Used to ramp.
adkims (strict) / r (relaxed)DKIM alignment mode. Relaxed allows subdomains; strict requires exact match.
aspfs / rSPF alignment mode, same semantics.
fo1Forensic options. 1 = report if any mechanism fails; recommended over the default 0.
  • p=none: take no action (monitor only). This is where you start.
  • rua=mailto:...: point this at an inbox or a DMARC report processor you actually read. Receivers send one XML report per day per source.
  • Relaxed alignment (the default) treats mail.yourdomain.com and yourdomain.com as aligned. Strict requires an exact label match. Start relaxed.

Learn more: https://dmarc.org/overview/

Reading aggregate (RUA) reports

Aggregate reports are gzipped XML, one per reporting source per day. Each <record> block reports a sending IP, the message count, the disposition applied, and the SPF/DKIM/alignment results. Raw XML is unreadable at scale; feed it to a DMARC analytics processor (open-source parsers and hosted dashboards both exist) that groups by sending source so you can answer the only question that matters during rollout: which legitimate sources are not yet passing aligned?

A minimal mental model of one record:

Sample data (XML)
<record>
  <row>
    <source_ip>198.51.100.7</source_ip>
    <count>42</count>
    <policy_evaluated>
      <disposition>none</disposition>
      <dkim>pass</dkim>
      <spf>fail</spf>
    </policy_evaluated>
  </row>
  <identifiers><header_from>yourdomain.com</header_from></identifiers>
  <auth_results>
    <dkim><domain>yourdomain.com</domain><result>pass</result></dkim>
    <spf><domain>bounce.provider.example</domain><result>pass</result></spf>
  </auth_results>
</record>

Here SPF "passes" for the provider's bounce domain but does not align with header_from, so policy_evaluated/spf=fail. DKIM passes and aligns, so DMARC passes overall. This is the normal, healthy pattern for provider-relayed mail.

How do I roll out DMARC without breaking my email?

Roll out in three stages, never jumping straight to p=reject. The staged path is: p=none (monitor), then p=quarantine; pct=25, then p=reject.

Jumping straight to p=reject before you understand your own sending will silently destroy legitimate mail you forgot about (the CRM, the invoicing tool, the old marketing platform, the helpdesk, the calendar invites). The safe path is:

  1. p=none (monitor). Collect aggregate reports for a few weeks. Identify every legitimate source sending as your domain and get them all passing SPF/DKIM with alignment.
  2. p=quarantine; pct=25 (quarantine a slice). Failing mail goes to spam, but only for 25% of traffic, limiting blast radius while you watch for fallout. Increase pct gradually toward 100.
  3. p=reject (enforce). Failing mail is rejected outright. This is the end state that actually stops spoofing of your brand.

A realistic timeline:

StageRecordDurationExit criterion
Monitorp=none; rua=...; fo=12–4 weeksEvery legitimate source identified and aligned
Quarantine 25%p=quarantine; pct=251 weekNo legitimate mail in spam in reports
Quarantine 100%p=quarantine1 weekStill clean
Rejectp=rejectpermanent,

Do not forget the sp tag. A p=reject on the apex with no sp still leaves arbitrary *.yourdomain.com subdomains spoofable under the default (subdomains inherit p, but if you ever relax the apex you must reconsider). Setting sp=reject explicitly closes subdomain spoofing.

The forwarding and mailing-list problem

DMARC at p=reject breaks two legitimate cases: plain forwarding (SPF fails at the next hop; DKIM may survive) and mailing lists that rewrite the body or subject (DKIM bh/header breaks; SPF is the list's). The standards answer is ARC (RFC 8617), covered below: it lets the forwarder vouch that authentication passed before it touched the message. Until ARC is universal, expect a small amount of legitimate forwarded mail to be affected at reject, which is one reason marketing and transactional streams that are heavily forwarded warrant extra care.

Common mistake

Stopping at p=none forever. Monitoring alone does not protect you from spoofing; only quarantine or reject does. A second common mistake is enabling p=reject while a forgotten third-party tool still sends unaligned mail as your domain, kill that source or get it aligned before you enforce.

DMARCbis (RFC 9989): what the latest standard changes

In May 2026 the IETF published DMARCbis as RFC 9989 (Proposed Standard), replacing the informational RFC 7489 from 2015 as the authoritative reference for DMARC. Reporting was split into two companion documents: RFC 9990 (aggregate reports) and RFC 9991 (failure reports).

The headline: this is not a breaking change. Your existing v=DMARC1 records keep working and you don't need to republish anything. DMARCbis mostly resolves ambiguities, standardizes receiver behavior, and closes gaps that RFC 7489 left to interpretation.

What actually changes

  • Organizational-domain discovery via a "DNS Tree Walk" instead of the Public Suffix List. RFC 7489 relied on the Public Suffix List (PSL) to find the organizational-domain boundary. DMARCbis replaces that by walking the DNS tree upward (querying _dmarc at each label), giving consistent, predictable boundaries even with nested subdomains, without depending on an external, manually-maintained list.
  • New np tag, policy for non-existent subdomains. Lets you explicitly reject mail from subdomains that have no records (were never used for sending). This closes a common spoofing vector: np=reject blocks forged subdomains without touching your live streams.
  • New psd tag, public suffix domain flag. For registry / PSD (Public Suffix Domain) operators that want to publish DMARC at the suffix level. A normal sender does not set it.
  • New t (testing) tag in place of pct. t=y means testing mode (receivers don't enforce the policy, even at p=reject), and t=n means full enforcement. This replaces the unreliable percentage mechanism of the old pct tag.
  • Removed tags: pct, rf, ri. pct (percentage of mail the policy applies to) was applied inconsistently by receivers, and values other than 0 and 100 were unreliable, hence its replacement by the binary t. rf (failure-report format) and ri (report interval) are gone; reporting details now live in RFC 9990/9991.
  • SPF counts toward DMARC only from MAIL FROM. DMARCbis removes the old HELO fallback, for SPF alignment, only the MAIL FROM (Return-Path) domain counts.

What to do now

  • Don't panic or republish everything. Existing records stay valid.
  • Stop relying on pct. If you use it for gradual rollout, plan a move to t=y / t=n and stage via p and sp (see the rollout path in the RFC 7489 section above).
  • Consider adding np=reject. It's a cheap, strong defense against spoofing of unused subdomains.
  • Don't treat p=none as a destination. The standard's intent (and the bulk-sender rules) is to reach enforcement (quarantine / reject).

Learn more: RFC 9989, RFC 9990, RFC 9991, plus the change summary at https://dmarc.org/.

How do I verify my SPF, DKIM, and DMARC setup?

Check DNS records directly with dig:

Verification commands
# SPF record (look for exactly one v=spf1 string)
dig TXT yourdomain.com +short

# DKIM record (replace 's1' with your provider's selector)
dig TXT s1._domainkey.yourdomain.com +short

# DMARC record
dig TXT _dmarc.yourdomain.com +short

# Follow a DKIM CNAME to the provider's hosted key
dig CNAME s1._domainkey.yourdomain.com +short

# Query a specific authoritative resolver to bypass local caching
dig TXT _dmarc.yourdomain.com @8.8.8.8 +short

Expected output: each command should return your configured record. No output means the record is missing.

How to use this in practice

  • Run these from a machine outside your network to see what the public internet sees. dig queries live DNS, so results reflect what receiving mail servers will resolve.
  • Empty output almost always means one of: the record was never published, it is on the wrong host or selector, or DNS has not propagated yet (see DNS TTL below).
  • Querying @8.8.8.8 or @1.1.1.1 directly shows you a public resolver's view, sidestepping a stale local cache.
  • If dig is not available, the same lookups work through web tools like MXToolbox (see troubleshooting).
  • After any DNS change, remember that propagation is bounded by the record's TTL. A record with a 3600s TTL can be cached for up to an hour before your edit is visible everywhere.

Verifying the full chain with the message headers

The fastest functional check is to send a real message to any mailbox and read the Authentication-Results header the receiver adds:

Authentication-Results: mx.google.com;
  dkim=pass header.d=yourdomain.com;
  spf=pass (google.com: domain of bounce@provider.example designates 198.51.100.7) smtp.mailfrom=bounce@provider.example;
  dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=yourdomain.com

Confirm all three: dkim=pass with header.d = your domain, spf=pass, and dmarc=pass with header.from = your domain. If dmarc=pass but dkim aligns to the provider's domain rather than yours, so DMARC is passing only on an aligned SPF Return-Path, you will pass DMARC today but lose alignment the moment forwarding strips SPF, because nothing aligned survives the hop. Fix the DKIM d= so the durable mechanism carries alignment.

Authentication checklist

  • Exactly one SPF TXT record, with ~all, under the 10 DNS-lookup limit (count the expanded lookups)
  • DKIM public key published at the provider's selector and verified in the provider dashboard
  • DKIM signs with d=yourdomain.com (aligned), not the provider's domain
  • DMARC record present, starting at p=none with a working rua mailbox and fo=1
  • dig returns each record from outside your network
  • A test send shows dkim=pass, spf=pass, and dmarc=pass in Authentication-Results (or score it with mail-tester.com)
  • sp policy set when you reach enforcement

What is sender reputation and why does it matter?

Sender reputation reflects how you behave over time, where authentication only proves who you are. Mailbox providers track, per domain and per IP, how recipients react to your mail: opens, deletes without reading, "this is spam" clicks, bounces, and how often you hit known spam traps.

A strong reputation buys inbox placement. A damaged one sends even perfectly authenticated mail to spam. Reputation attaches to both the sending domain and the sending IP, and it recovers slowly, so it is much easier to protect than to repair.

Domain reputation vs. IP reputation

These are tracked separately and you should reason about them separately.

  • Domain reputation follows the From:/DKIM d= domain wherever it sends. It is the lever you control on shared infrastructure (most providers put small senders on shared IP pools). It cannot be escaped by changing IPs.
  • IP reputation follows the sending IP. On a shared IP you inherit the pool's reputation, good when the provider polices the pool, bad when a noisy neighbour torches it. On a dedicated IP you own the reputation entirely, which is only worth it above a sustained volume (commonly cited as tens of thousands of messages per day) because a dedicated IP that sends too little never accumulates enough signal to build trust.
Shared IPDedicated IP
Who controls reputationThe pool (provider + neighbours)You alone
Warm-up neededMinimal (pool is already warm)Yes, mandatory
Min. sustainable volumeAnyHigh and consistent
Best forLow/variable volume, most SaaSHigh, steady volume; strict isolation needs

Spam traps

Spam traps are addresses that should never receive mail. Pristine traps are addresses never used by a human; hitting one means you acquired addresses without consent. Recycled traps are formerly-real addresses a provider reclaimed after long dormancy; hitting one means you do not prune inactive subscribers. Both are reputation poison and both are avoided by the same disciplines: real consent at capture (see Email Capture) and aggressive removal of inactive addresses (see List Management).

How do I warm a new IP or domain?

Gradually increase volume so providers can observe positive engagement before you send at scale. A new IP or domain has no reputation. A fresh sender that suddenly blasts 100,000 emails looks exactly like a spammer who just bought a new IP.

WeekDaily Volume
150-100
2200-500
31,000-2,000
45,000-10,000

Start with engaged users. Send consistently. Do not rush.

Learn more: Google's sender guidelines (https://support.google.com/mail/answer/81126) and your provider's IP/domain warm-up schedule. The principle, start small, ramp gradually, is universal.

How to do it well

  • Start with your most engaged users. People who open and click send the strongest positive signals. Warming on your best audience teaches providers that your mail is wanted.
  • Send consistently. Erratic volume (big spike, then silence, then another spike) reads as unpredictable and untrustworthy. Steady daily cadence is the goal.
  • Do not rush the ramp. If engagement metrics dip or complaints rise during a week, hold volume flat (or step back) rather than escalating. The table is a guideline, not a deadline.
  • Warm the pair that matters. Reputation attaches to both the sending IP and the sending domain. If you are on shared IPs (most providers), domain warming is the lever you control.
  • Warm per receiving provider. Gmail, Yahoo/AOL, and Microsoft (Outlook/Hotmail/Office 365) each maintain independent reputation. A ramp that satisfies Gmail may still trip Microsoft's stricter throttling. Watch each one's postmaster signals separately.

Reading throttling signals during warm-up

While ramping, receivers will tell you when you are going too fast through SMTP responses. Treat these as backpressure, not errors:

  • 421 4.7.0 / "too many connections" / "rate limited" → slow your send rate; do not retry aggressively.
  • 4.2.1 "mailbox temporarily disabled" or deferral → back off; the receiver is testing you.
  • Sudden rise in messages landing in spam (visible in postmaster tools) → hold volume flat for several days before ramping again.

If you see deferrals, reducing volume for a few days and then resuming the ramp recovers faster than pushing through.

How do I keep my reputation healthy once it is built?

Send to people who want your mail, and react to every bounce and complaint. The short version:

Do:

  • Send to engaged users.
  • Keep bounce rate under 4%.
  • Keep complaint rate under 0.1%.
  • Remove inactive subscribers.

Do not:

  • Send to purchased lists.
  • Ignore bounces or complaints.
  • Send inconsistent volumes.

Why each rule exists

  • Send to engaged users: engagement (opens, clicks, replies) is the single biggest positive signal. Sending to people who ignore you drags reputation down even if they never complain. Many teams adopt a sunset policy: stop mailing addresses with no engagement for, say, 90–180 days, and remove them after a re-permission attempt.
  • Keep bounce under 4%, complaints under 0.1%: these are the thresholds at which providers start throttling or blocking. Treat them as hard ceilings, not targets. The bounce and complaint sections below define tighter goals.
  • Remove inactive subscribers: dead and unengaged addresses lower your engagement rate and risk hitting recycled spam traps. Pruning them improves deliverability for everyone who remains.
  • Never send to purchased lists: bought lists are full of spam traps and people who never opted in. They generate complaints and bounces that can blacklist you in a single send.
  • Never ignore bounces or complaints: a sender who keeps mailing addresses that bounce or complain is the clearest signature of a spammer. Automated suppression (see List Management) is mandatory.

What is a bounce, and how should I handle hard versus soft bounces?

A bounce is the receiving server telling you it could not deliver your message. How you react depends on whether the failure is permanent or temporary.

TypeCauseAction
Hard bouncePermanent failure to deliverRemove immediately
Soft bounceTransient failure to deliverRetry: 1h to 4h to 24h, remove after 3 to 5 failures

Hard bounces are permanent. The mailbox does not exist, the domain is invalid, or the address was disabled. There is no point retrying; the address will never accept mail. Continuing to send to it is one of the loudest "I do not clean my list" signals a provider can see, so suppress it immediately and permanently.

Soft bounces are temporary. A full mailbox, a server that is down, a momentary greylist. These may succeed later, so retry with increasing backoff (1h, then 4h, then 24h). But persistent soft bounces (3 to 5 failures) behave like hard bounces in practice: suppress the address rather than retrying forever.

A bounce arrives
Hard bounce

5xx: “user unknown”, bad domain. Permanent.

→ Suppress immediately and permanently.

Soft bounce

4xx: mailbox full, transient issue. Temporary.

→ Retry: 1h → 4h → 24h.

After ≥5 attempts: suppress.

Policy rejection

4.7.x / 5.7.x: reputation, DMARC, content.

→ Not an address problem. Investigate authentication and reputation.

Classify the bounce before you react: an address failure (suppress) is not the same as a sender failure (fix your setup).

Reading SMTP status codes

The bounce message carries an SMTP reply code and usually a richer RFC 3463 enhanced status code (class.subject.detail). Use the enhanced code to classify:

CodeClassTreat as
5xx (e.g. 550 5.1.1)Permanent, "user unknown"Hard bounce, suppress now
5.1.1Bad destination mailboxHard
5.1.10Address does not exist (null MX)Hard
4xx (e.g. 452 4.2.2)TransientSoft bounce, retry with backoff
4.2.2Mailbox fullSoft
4.7.x / 5.7.xPolicy / reputation rejectionNot a list problem, investigate authentication/reputation

A 5.7.1 "message rejected due to policy" is not an invalid address; suppressing the recipient hides a sender-side problem (failed DMARC, blocklisted IP, content). Distinguish address failures (suppress the recipient) from sender failures (fix your setup).

Classifying bounces in code (provider-neutral)

Most providers deliver bounce events to a webhook. Normalize the event, then branch on the classification, never hardcode one provider's field names beyond the thin adapter that produces this shape:

Code for your engineers (TypeScript)
type BounceEvent = {
  email: string;
  type: "hard" | "soft" | "block"; // normalized in your provider adapter
  smtpCode?: string;               // e.g. "550 5.1.1"
  diagnostic?: string;
};

async function handleBounce(e: BounceEvent) {
  switch (e.type) {
    case "hard":
    case "block":
      // Permanent address failure: never send here again.
      await suppressionList.add(e.email, { reason: e.type, code: e.smtpCode });
      break;
    case "soft": {
      const failures = await softBounceCount.increment(e.email);
      if (failures >= 5) {
        await suppressionList.add(e.email, { reason: "repeated-soft", code: e.smtpCode });
      }
      // else: provider's retry schedule will try again; nothing to do.
      break;
    }
  }
}

The suppression list must be consulted at send time, in your application, independent of any provider's own suppression, see List Management for the suppression model and Sending Reliability for retry/backoff mechanics.

What bounce rate is too high?

Use these targets: under 1% is good, 1 to 3% is acceptable, 3 to 4% is concerning, and over 4% is critical.

How to read the targets: a bounce rate under 1% is healthy and indicates a clean, recently validated list. 1 to 3% is tolerable but worth watching. Above 3% providers begin to distrust you. Above 4% you risk active throttling and blocks. A sudden jump usually means a bad import, a stale list segment, or a validation step that broke. Investigate the source of the addresses, not just the symptom.

Practical guidance: do not wait until send time to discover bad addresses. Validate at capture (see Email Capture) and run pre-send suppression checks. Bounce processing should be automated through webhooks so suppression happens in real time, not in a weekly manual sweep, see Webhooks & Events for receiving and verifying those events.

What is a complaint, and what complaint rate is too high?

A complaint is a recipient clicking "report spam." Complaints are far more damaging per event than bounces, because they are a direct, recipient-driven signal that your mail is unwanted.

Use these targets: under 0.01% is excellent, 0.01 to 0.05% is good, and over 0.05% is critical.

Note how much tighter these thresholds are than bounce thresholds. Even a 0.05% complaint rate (1 in 2,000) is a red line. The 2024 bulk-sender rules make this concrete: Gmail requires bulk senders to stay below 0.3% spam complaints and strongly recommends staying under 0.1%, and rewards senders who stay near 0.01%. Microsoft and Yahoo apply comparable expectations. Treat 0.3% as the cliff and 0.1% as your real ceiling.

Measurement note: the spam rate that matters is the one Google Postmaster Tools reports (complaints as a fraction of mail that reached the inbox-eligible population), not your own opens-based estimate. Track the provider's number, because that is the number used against you.

How do I reduce complaints?

Make sure people remember signing up and can leave easily:

  • Only send to opted-in users.
  • Make unsubscribe easy and immediate.
  • Use clear sender names and From addresses.

Why these work:

  • Only send to opted-in users: the most common reason people hit "spam" is that they do not remember signing up. Real, explicit consent (ideally double opt-in, see Email Capture) prevents the surprise that triggers complaints.
  • Make unsubscribe easy and immediate: if leaving is hard, the spam button becomes the unsubscribe button, and that one click hurts you far more than a quiet opt-out. A visible, one-click unsubscribe that takes effect immediately is both a legal requirement and a deliverability protection (see one-click unsubscribe below and Compliance).
  • Use clear sender names and From addresses: recipients complain about mail they do not recognize. A consistent, recognizable From: name and address builds familiarity and trust.

What is a feedback loop and how do I set one up?

A feedback loop (FBL) is a program where the mailbox provider forwards you a notification whenever a recipient marks your mail as spam. Register for them and remove complainers immediately:

  • Google does not run a per-message FBL; it exposes complaint rates through Postmaster Tools and offers a header-based FBL for high-volume senders who tag mail with a Feedback-ID header.
  • Yahoo / AOL (Verizon Media) run Complaint Feedback Loop (CFL) programs you enrol in by sending domain.
  • Microsoft offers SNDS (Smart Network Data Services) and the JMRP (Junk Mail Reporting Program) feedback loop.

Registering for these is how you learn about complaints at all. Without them you are flying blind. Add a Feedback-ID header to bulk mail so Google can attribute complaints back to a campaign/stream:

Feedback-ID: newsletter:campaign42:streamA:yourdomain

The moment a complaint arrives, suppress that address forever; never give a complainer the chance to complain twice. Wire FBL/complaint events into the same suppression pipeline as bounces (see Webhooks & Events).

What did the Gmail/Yahoo/Microsoft 2024 bulk-sender rules actually require?

Effective February 2024, Gmail and Yahoo (with Microsoft following the same direction) imposed enforced requirements on bulk senders, broadly, those sending 5,000+ messages per day to a given provider, though the authentication requirements increasingly apply to everyone. Three requirements stand out:

  1. Authenticate everything. Bulk senders must pass SPF and DKIM, and publish a DMARC record (at minimum p=none) with alignment. Unauthenticated bulk mail is rejected or junked.
  2. Stay under the spam-complaint ceiling. Keep the Postmaster-reported spam rate below 0.3%, and ideally under 0.1%. Cross 0.3% and you face throttling and spam-foldering until you recover.
  3. Offer one-click unsubscribe. Bulk marketing/promotional mail must include a working one-click unsubscribe (RFC 8058) and honour opt-outs within 2 days.
RequirementApplies toRFC / standardFailure consequence
SPF + DKIM passAll bulk senders7208 / 6376Reject / junk
DMARC published, alignedAll bulk senders7489Reject / junk
Spam rate < 0.3%All bulk sendersPostmaster Tools metricThrottle / junk
One-click unsubscribeBulk marketing mail8058Junk; non-compliance
Valid forward/reverse DNS (PTR) on sending IPAll bulk senders,Reject
TLS for transmissionAll bulk sendersSTARTTLSReject

One-click unsubscribe (RFC 8058) done correctly

One-click unsubscribe requires two headers working together. The List-Unsubscribe header (RFC 2369) must offer an HTTPS URL, and the List-Unsubscribe-Post header (RFC 8058) signals that a single HTTP POST to that URL, with no further user interaction, no login, no confirmation page, must perform the unsubscribe.

List-Unsubscribe: <https://yourdomain.com/u/abc123>, <mailto:unsubscribe@yourdomain.com?subject=unsub-abc123>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

Rules that trip people up:

  • The HTTPS variant must accept a POST (the mailbox provider posts on the user's behalf). Do not require a GET, a landing page, or authentication.
  • Include the mailto: variant too, as a fallback.
  • The headers must be covered by your DKIM signature (h=...:list-unsubscribe:list-unsubscribe-post) or a relay could strip/alter them.
  • Honour the opt-out within 2 days (do so immediately in practice).
  • One-click unsubscribe is for bulk/marketing mail. Genuinely transactional mail (password resets, receipts) is exempt, but see Email Types and Compliance for where the line actually falls; mixing a promo into a receipt can pull the whole message under the requirement.

A minimal provider-neutral handler:

Code for your engineers (TypeScript)
// POST /u/:token , one-click unsubscribe endpoint (no auth, idempotent)
app.post("/u/:token", async (req, res) => {
  const sub = await tokens.resolve(req.params.token); // opaque, signed, single-purpose
  if (!sub) return res.sendStatus(404);
  await suppressionList.add(sub.email, { reason: "unsubscribe", stream: sub.stream });
  // Respond 200 immediately; never redirect to a login or confirmation page.
  return res.sendStatus(200);
});

The same endpoint should also accept GET for humans who click the visible in-body unsubscribe link, but the POST path is what satisfies RFC 8058.

BIMI: showing your logo in the inbox

BIMI (Brand Indicators for Message Identification) lets compliant mailbox providers display your brand logo next to authenticated mail. It is not an authentication mechanism and adds no deliverability boost by itself, but it requires DMARC at enforcement (p=quarantine or p=reject), so it is a useful forcing function and a visible reward for reaching enforcement.

Requirements:

  • DMARC policy of p=quarantine (with pct=100) or p=reject. p=none does not qualify.
  • A logo as an SVG Tiny PS (Portable/Secure profile), square, hosted over HTTPS.
  • For most providers (notably Gmail and Apple Mail), a VMC (Verified Mark Certificate) or CMC (Common Mark Certificate), a certificate from an authorized CA attesting you own the mark. This is the costly, slow part.
; TXT record at default._bimi.yourdomain.com
default._bimi.yourdomain.com.  IN  TXT  "v=BIMI1; l=https://yourdomain.com/bimi/logo.svg; a=https://yourdomain.com/bimi/vmc.pem"
  • l=: HTTPS URL of the SVG logo.
  • a=: HTTPS URL of the VMC/CMC PEM bundle. Omit only if you target providers that accept BIMI without a certificate (fewer do).

Treat BIMI as the capstone of a finished authentication program, not an early task.

ARC: surviving forwarding and mailing lists

ARC (Authenticated Received Chain, RFC 8617) repairs the DMARC-versus-forwarding problem. When an intermediary (a mailing list, a forwarding service, a security gateway) modifies a message and thereby breaks SPF/DKIM, ARC lets that intermediary record "authentication passed when I received it" in a tamper-evident chain of headers. A downstream receiver that trusts the intermediary can then honour the original pass even though SPF/DKIM now fail.

ARC adds three headers per hop: ARC-Authentication-Results, ARC-Message-Signature, and ARC-Seal. Each hop seals the chain so far, so the receiver can verify the chain was not forged.

What you actually do about ARC:

  • As an originating sender: nothing directly, you cannot make a receiver trust an intermediary. Your job is to keep your own DKIM intact (relaxed canonicalization, sign stable headers) so fewer messages need ARC in the first place.
  • As a forwarder/intermediary (e.g. you run a service that re-sends others' mail): implement ARC sealing so downstream receivers can preserve the original DMARC result.
  • As a receiver: verify ARC chains from senders you trust before applying DMARC policy.

ARC is the reason a forwarded message can still reach the inbox under your p=reject. It is not something you "turn on" in DNS; it is implemented in the MTA.

MTA-STS and TLS-RPT: enforcing transport encryption

SMTP's opportunistic TLS (STARTTLS) is trivially downgraded by an active attacker who strips the STARTTLS capability, forcing plaintext. Two standards close this for inbound mail to your domain.

MTA-STS (RFC 8461)

MTA-STS lets you publish a policy that says "senders must use TLS with a valid certificate to deliver to my MX hosts." It is advertised via a DNS TXT record that points senders to an HTTPS-hosted policy file.

; TXT record at _mta-sts.yourdomain.com
_mta-sts.yourdomain.com.  IN  TXT  "v=STSv1; id=20240601000000"

The policy itself is served at https://mta-sts.yourdomain.com/.well-known/mta-sts.txt:

version: STSv1
mode: enforce
mx: mail.yourdomain.com
mx: *.mail.yourdomain.com
max_age: 604800
  • mode: testing first (failures are reported but mail still flows), then mode: enforce once TLS-RPT shows clean reports.
  • id= changes whenever you change the policy; bump it to invalidate caches.
  • max_age is how long senders cache the policy (seconds). One week is typical.

MTA-STS protects mail coming to you. It is the application-layer cousin of DANE/DNSSEC (which protects the same thing using DNSSEC-signed TLSA records); MTA-STS is usually easier to deploy because it does not require DNSSEC.

TLS-RPT (RFC 8460)

TLS-RPT asks senders to report TLS negotiation failures and policy problems, so you find out before MTA-STS enforce starts dropping mail.

; TXT record at _smtp._tls.yourdomain.com
_smtp._tls.yourdomain.com.  IN  TXT  "v=TLSRPTv1; rua=mailto:tls-reports@yourdomain.com"

Deploy TLS-RPT alongside MTA-STS in testing mode, read the JSON reports for a couple of weeks, fix any cert/MX issues they surface, then switch MTA-STS to enforce.

How should I structure my sending domains and subdomains?

Use different subdomains for different sending purposes, for example t.yourdomain.com for transactional emails and m.yourdomain.com for marketing emails. The way you structure your sending domains determines how problems in one stream affect others. A little architecture up front prevents a marketing misfire from taking down password-reset emails.

Why subdomain separation matters: reputation is tracked per sending domain. Marketing mail is inherently riskier, with higher volume, more complaints, and more unsubscribes. If you send marketing from the same domain as your transactional mail, a marketing reputation hit can drag your password resets and receipts into spam too. Isolating streams on separate subdomains (t. transactional, m. marketing) walls off the risk: even if m.yourdomain.com takes a reputation hit, t.yourdomain.com stays clean. This mirrors the transactional and marketing list separation in List Management.

A worked subdomain architecture

SubdomainStreamRiskDMARC posture
yourdomain.com (apex)Human/corporate mail, your brand identityHighest spoofing valuep=reject, sp=reject
t.yourdomain.comTransactional (receipts, resets, alerts)Low complaint, high deliverability needp=reject
m.yourdomain.comMarketing / newslettersHigher complaints/unsubsp=reject
notify.yourdomain.comThird-party tools (helpdesk, status page)Variablep=reject once aligned

Key rules:

  • Never send any program mail from the apex if you can avoid it. Keep the apex's reputation pristine and protect it with p=reject so it is hard to spoof in phishing.
  • Each subdomain gets its own SPF, DKIM, and DMARC. A subdomain inherits the org-level DMARC only via sp; publish explicit records so you can tune alignment and reporting per stream.
  • DKIM d= should match the sending subdomain (d=m.yourdomain.com) so reputation accrues to the right stream.
  • Because DMARC alignment is relaxed by default, m.yourdomain.com mail still aligns with a From: news@m.yourdomain.com. Keep the From: on the same subdomain you sign with.

What DNS TTL should I use for email records?

Set a low TTL (300s) during setup, then a high TTL (3600s or more) once things are stable. TTL (time-to-live) controls how long resolvers cache a DNS record.

During setup you will be editing records repeatedly and want changes to propagate fast, so set a low TTL (300s) and your fixes go live within minutes. Once everything is stable, raise the TTL (3600s or more). High TTL means fewer DNS lookups, faster resolution for receivers, and less load. Remember that any future change will then take up to that long to propagate, so drop the TTL back to 300s a day before you plan to edit a record.

TTL by record type

RecordSetup TTLStable TTLNote
SPF (TXT)3003600Lower before adding/removing a vendor
DKIM (TXT/CNAME)3003600CNAME-to-provider keys can stay high; the provider rotates behind it
DMARC (TXT)3003600Lower before a policy bump
MX3003600+Changing MX mid-flight risks lost mail; lower TTL a day ahead
MTA-STS policy idn/an/aCache controlled by max_age, not DNS TTL

The pattern is always the same: lower the TTL the day before a planned change, make the change, confirm propagation with dig against a public resolver, then raise it back.

My emails are going to spam: what do I check, and in what order?

Check in this order, from most common and most fixable to most subtle:

  1. Authentication (SPF, DKIM, DMARC).
  2. List-Unsubscribe header / one-click unsubscribe, required by Gmail/Yahoo since February 2024 (see Compliance).
  3. Sender reputation (blacklists, complaint rates).
  4. Content.
  5. Sending patterns (sudden volume spikes).

Why this order: the overwhelming majority of "going to spam" problems are authentication failures (number 1), so fix those first because nothing downstream matters until mail authenticates. The List-Unsubscribe header (number 2) is now mandatory for bulk senders to Gmail/Yahoo, and its absence alone can route you to spam. Only once identity and headers are correct should you investigate reputation (number 3), then content (number 4), and finally sending patterns (number 5) such as sudden volume spikes that look like a compromised account.

Troubleshooting decision tree

  • Mail to all providers suddenly failing? → Almost always DNS/authentication. Check dig for SPF/DKIM/DMARC; check for an expired DKIM selector or a duplicated SPF record. Check the IP against RBLs.
  • Mail to one provider (e.g. only Microsoft) failing? → Provider-specific reputation or throttling. Read that provider's postmaster signals (SNDS/JMRP for Microsoft, Postmaster Tools for Google). Look for 4.7.x/5.7.x rejections.
  • dkim=pass but dmarc=fail? → Alignment. Your DKIM d= or SPF Return-Path domain does not match From:. Fix the signing domain or the From:.
  • Was passing, broke after an edit? → Diff your DNS. A second SPF record, a typo'd selector, a TTL still serving the old value, or someone "cleaned up" a TXT record.
  • Forwarded mail failing only? → DKIM body/header modification or no ARC. Confirm relaxed canonicalization and that you sign stable headers.
  • High complaints with good auth? → Consent/content problem, not a technical one. Audit your opt-in source and unsubscribe path.
  • New domain/IP, low inbox rate? → Insufficient warm-up. Slow down and ramp on engaged users.

Which tools should I use to diagnose deliverability?

Use these, each for a different job:

  • Google Postmaster Tools: domain/IP reputation, spam rates, and authentication pass rates from Gmail's perspective.
  • mail-tester.com: send a test email, get a 0–10 deliverability score plus a content/auth/blacklist breakdown.
  • MXToolbox: check blacklist (RBL) status and run SPF/DKIM/DMARC lookups without dig.
  • Microsoft SNDS / JMRP: IP data and complaint feedback for Outlook/Hotmail/Office 365.
  • A DMARC aggregate-report processor: turns raw RUA XML into a per-source view of who is and is not aligned.

How to use each:

  • Google Postmaster Tools: connect your domain to see, straight from Gmail, your domain and IP reputation, spam rate, authentication pass rates, and FBL data. This is the closest thing to seeing yourself through Gmail's eyes; check it whenever delivery to Gmail dips.
  • mail-tester.com: send one email to the address it gives you and receive a 0 to 10 score plus a breakdown of authentication, content, and blacklist issues. Ideal for a quick pre-launch sanity check.
  • MXToolbox blacklist check: confirms whether your sending IP or domain appears on major blacklists (RBLs). If you are blacklisted, follow each list's delisting process after fixing the behaviour that caused it, delisting before the fix gets you re-listed.

Common mistakes that send good mail to spam

  • Two SPF records, or one SPF record over the 10-lookup limit (permerror).
  • DKIM signing with the provider's domain, so DMARC never aligns.
  • Sitting at p=none forever and assuming you are "DMARC compliant."
  • Mixing marketing and transactional on one domain, then watching a campaign sink receipts.
  • No suppression at send time, so bounced and complained addresses get re-mailed.
  • Missing or GET-only unsubscribe, failing RFC 8058.
  • Image-only emails, link shorteners, mismatched From: display names, or a From: domain that does not match the signing domain.
  • Cold-blasting a new IP/domain with no warm-up.
  • Treating 4.7.x/5.7.x policy rejections as bad addresses and suppressing the recipient instead of fixing the sender-side cause.

What is the full deliverability checklist?

  • SPF, DKIM, and DMARC all pass with alignment (dkim=pass, spf=pass, dmarc=pass in Authentication-Results)
  • Exactly one SPF record, under the 10-lookup limit, ~all (or -all at enforcement)
  • DKIM signs with your own domain (d=) and a 2048-bit key
  • DMARC at p=quarantine/p=reject with sp set, rua monitored
  • List-Unsubscribe + List-Unsubscribe-Post (RFC 8058) on bulk mail; honoured within 2 days
  • Spam complaint rate under 0.3% (target under 0.1%); bounce rate under 1–2%
  • Hard bounces and complaints suppressed automatically in real time
  • Transactional and marketing isolated on separate subdomains, never the apex
  • Postmaster Tools, SNDS/JMRP, and FBLs connected and monitored
  • New IP or domain warmed gradually before high-volume sending
  • (Optional, capstone) MTA-STS + TLS-RPT for inbound TLS; BIMI once at enforcement
  • List Management: handle bounces and complaints to protect reputation.
  • Sending Reliability: retry logic and error handling.
  • Webhooks & Events: receive and verify bounce/complaint events that feed suppression.
  • Compliance: List-Unsubscribe header, one-click unsubscribe, and legal requirements.
  • Email Capture: validate addresses before they ever bounce.
  • Email Types: the transactional vs. marketing line that decides which rules apply.
  • Index: back to the guide overview.

If this is happening in production…

Don't guess. Get a read on it

Book a 30-min reviewWe'll look at where your mail goes today and what it would take to fix it (no obligation).Book a deliverability call

Seen this in the wild: read the case studies