How You Lose Domain Reputation (and How to Get It Back)
Receivers judge your domain on whether each message authenticated, how recipients reacted to it, and how you behave over time. No provider publishes that judgement as a number. Infer it from DMARC aggregate reports, blocklist listings and complaint logs.
Do you need this?
| Your domain | Do this |
|---|---|
| Sends mail | SPF, DKIM, DMARC, and read the rua= reports |
| Sends no mail | Lock it down: MX 0 ., v=spf1 -all, p=reject. No DKIM needed |
A domain that sends nothing still needs the lockdown, or anyone can send as it.
What breaks
| Cause | In the report | Fix |
|---|---|---|
| ESP sends from its own envelope domain | auth_results/spf=pass, policy_evaluated/spf=fail | Bounce domain under your org domain, or DKIM d= yours |
| Key never published | policy_evaluated/dkim=fail, no auth_results/dkim | Publish <selector>._domainkey |
Selector left with empty p= | same | A revoked key (RFC 6376 §3.6.1). Publish a real one |
p=none | reports arrive, nothing is blocked | Move to quarantine, then reject |
| Forwarding | SPF fails at the second hop | Nothing. DKIM survives it, SPF does not |
The alignment trap. Adding include: to your SPF record does not fix an ESP that sends from its own envelope domain: the receiver evaluates that domain’s SPF and never reads yours. Fix it at the sender, not in your record.
policy_evaluated is the aligned verdict, auth_results the raw one. Read the first, or you will edit the wrong record.
Losing reputation when authentication is fine
- Complaints. “Report spam” is the strongest negative signal, measured against volume.
- Unengaged or purchased lists. Complaints, spam-trap hits and bounces, together.
- Sudden volume. 200 messages a day, then 40,000, reads as suspicious whatever the authentication.
- A compromised mailbox or form relay. A common source of a new failing
source_ip. - Shared IPs. Your reputation is partly your neighbours’ until you have the volume for your own.
- No bounce hygiene. Backscatter and repeated hard bounces both count against you.
Reading the reports
Per day, from rua=:
- Unauthenticated volume — rows where both mechanisms are
fail. - Which
source_ip+header_frompairs fail, and whether each is expected. - The policy actually in force, from
policy_published.
Four limits belong with any conclusion:
- One report is one provider’s partial view. Two providers, same window, are not duplicates.
countis per window, not per day. Normalise.- A source that stops appearing may have been blocked, not fixed — ambiguous under
rejectorquarantine, clearer undernone. - Weight by volume. Ten messages a day is not ten thousand.
Fix order
- Inventory every sender: ESPs, transactional, ticketing, printers, CRMs, forms, alerts.
- Get each into SPF and signing with aligned DKIM, one selector per sender.
- Stay at
p=noneuntil every legitimate sender passes consistently. - Ramp
none→quarantine→reject, sampling withpct. - Set
sp=andnp=(RFC 9091) on purpose. - Watch per-day failures per source after each step. A rise means you enforced early.
- Fix the behaviour too: suppress hard bounces, stop mailing the unengaged, close the form relay.
pct is the share of failing mail the receiver may act on. The only hard rule is that it must not apply the policy to more than pct percent; the usual fallbacks (remainder quarantined under reject, untouched under quarantine) are SHOULD-level in RFC 7489 §6.6.4, so do not rely on them.
The records
; SPF — exactly one. Two v=spf1 records is a permerror, not a merge (RFC 7208 §4.5)
vibhuvioio.com. 300 IN TXT "v=spf1 include:_spf.example.net ip4:192.0.2.10 -all"
; DKIM — one per selector. Strings hold 255 octets, so long keys are concatenated
sel2026._domainkey.vibhuvioio.com. 300 IN TXT ( "v=DKIM1; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...w==" )
; A deliberately revoked selector: empty p= (RFC 6376 §3.6.1)
sel2025._domainkey.vibhuvioio.com. 300 IN TXT "v=DKIM1; k=rsa; p="
; DMARC
_dmarc.vibhuvioio.com. 300 IN TXT "v=DMARC1; p=reject; rua=mailto:dmarc@vibhuvioio.com"
; An rua outside your domain must authorise the reports (RFC 7489 §7.1)
vibhuvioio.com._report._dmarc.reports.example.net. 300 IN TXT "v=DMARC1"
; MX, and a null MX for a domain that must not receive (RFC 7505)
vibhuvioio.com. 300 IN MX 10 mx1.mail.example.net.
no-reply.vibhuvioio.com. 300 IN MX 0 .
dig +short TXT vibhuvioio.com
dig +short TXT sel2026._domainkey.vibhuvioio.com
dig +short TXT _dmarc.vibhuvioio.com
dig +short MX vibhuvioio.com
Is your setup current?
| Standard | Current | Note |
|---|---|---|
| SPF | RFC 7208 | Obsoletes 4408. 10-term limit, at most 2 void lookups |
| DKIM | RFC 6376 | Updated by RFC 8301 (keys ≥1024, prefer 2048) and RFC 8463 (Ed25519) |
| DMARC | RFC 7489 | Informational, not a standard. The DMARCbis revision is in progress and clarifies alignment — track it |
np= | RFC 9091 | Policy for non-existent subdomains. Newer; worth adopting |
| ARC | RFC 8617 | Carries authentication across a forwarder |
| MTA-STS | RFC 8461 | The layer past DMARC: enforces TLS to your MX |
| TLS-RPT | RFC 8460 | Reporting for MTA-STS and TLS failures |
| DANE for SMTP | RFC 7672 | TLS pinning in DNS. Needs DNSSEC |
| DNSSEC | RFC 4033–4035 | Makes the answers SPF and DKIM depend on verifiable |
The common gap: DMARC alone, no MTA-STS or TLS-RPT, and SPF/DKIM set once and never re-checked. DKIM keys age, selectors get retired, and a sender appears that nobody inventoried.
Checklist
- One SPF record, under 10 terms, ending
-all. - Every selector published; retired ones revoked with an empty
p=. adkim/aspfchosen on purpose.- Unauthenticated volume per day, per source, and its direction.
sp=andnp=deliberate.- Complaints and bounces reviewed alongside authentication.
- DNSSEC automated and owned, or consciously off.