When legitimate mail starts bouncing or landing in spam with a DMARC failure, the cause is almost never DMARC itself: it is an SPF or DKIM result that did not pass, or a pass that did not align with the From domain. This guide is a diagnostic reference for that moment. It works from the symptom (a bounce code, an Authentication-Results header, a line in an aggregate report) to the likely causes in order of frequency, shows where to look for each one, and gives the fix and how to stop it coming back. It is for email administrators, platform engineers and security teams who own a sending domain. Concepts, record design and the rollout to p=reject are covered in the DMARC, SPF and DKIM Guide, which this page does not repeat. Result names, error strings and limits were checked against the RFCs and Google and Microsoft documentation on 11 November 2026.
Key takeaways
- Read the receiver's verdict before touching DNS. The Authentication-Results header (or Microsoft's
compauthreason code) tells you which mechanism failed and which domain it evaluated. Most wasted effort comes from editing the SPF record when DKIM, or alignment, was the problem. - A pass is not enough, it must align. Under RFC 9989, DMARC passes only when SPF (on the MAIL FROM domain) or DKIM (on the
d=domain) passes and that domain matches the From domain, relaxed or strict. A supplier passing SPF on its own bounce domain still fails DMARC for you. - SPF permerror is self-inflicted. More than 10 DNS-querying terms, more than two void lookups, or two SPF records all produce permerror under RFC 7208, which most receivers treat as a failure.
- DKIM body hash failures mean something changed the message. Footers, disclaimers, mailing lists and security gateways rewrite content after signing. Find the hop that changed it rather than weakening the signature.
- Forwarding breaks SPF by design. Plan for DKIM to carry DMARC through forwarders and lists, and treat ARC as a receiver's discretion, not a fix you control.
Step one: read the verdict the receiver gave
Every diagnosis starts from evidence the receiving system produced, not from a DNS lookup on your side. Get a full copy of an affected message with headers (from a test mailbox at the affected provider or from the recipient) or the bounce (non-delivery report) text, and read three things.
- The Authentication-Results header. RFC 8601 defines it. Read
spf=withsmtp.mailfrom(the domain SPF was checked against),dkim=withheader.dandheader.s(signing domain and selector, one result per signature), anddmarc=withheader.from. If SPF passed forbounces.supplier.exampleand DKIM passed forsupplier.examplewhile the From domain isyourdomain.example, both mechanisms worked and alignment is the fault. - Which hop wrote it. Only trust the Authentication-Results header added by the final receiver (its authserv-id names it). Headers inherited from earlier hops can be stale or forged.
- The bounce code. Microsoft consumer services (Outlook.com, Hotmail) return
550 5.7.515 Access denied, sending domain <domain> does not meet the required authentication level.to senders of 5,000 or more messages that fail its SPF, DKIM and aligned DMARC requirements. Google says unauthenticated messages to Gmail might be rejected with error 5.7.26. A DMARC rejection from any receiver usually names DMARC or the sender's policy in the text.
Result values and what they mean
| Mechanism | Result | Meaning (RFC 7208 / RFC 8601) | First place to look |
|---|---|---|---|
| SPF | fail | Explicit statement the client is not authorised (-all matched) | Sending IP missing from the record of the MAIL FROM domain |
| SPF | softfail | Weak statement the host is probably not authorised (~all) | Same as fail; the receiver weighs it less strongly |
| SPF | neutral / none | Owner asserts nothing (?all) / no SPF record retrieved | Wrong MAIL FROM domain, or record missing on that domain |
| SPF | permerror | Published records could not be correctly interpreted | Lookup count, void lookups, duplicate records, syntax |
| SPF | temperror | Transient, generally DNS, error during the check | Authoritative DNS reachability and response times |
| DKIM | fail | An acceptable signature failed verification | Body or header changed after signing, or wrong key published |
| DKIM | neutral | Signature had syntax errors or could not be processed | Malformed DKIM-Signature header from the signer |
| DKIM | policy | Signed, but signature not acceptable to local policy | Key length or algorithm below the receiver's minimum |
| DKIM | permerror / temperror | Unrecoverable / likely transient verification error | Key record missing or malformed / DNS timeout on the selector |
| DKIM | none | Message was not signed | Signing not enabled on this sending path |
| DMARC | fail | No aligned SPF or DKIM pass | Compare smtp.mailfrom and header.d with header.from |
Microsoft 365 composite authentication codes
Microsoft adds compauth= with a reason code to the Authentication-Results header. The ones that matter most when diagnosing, as documented on Microsoft Learn:
| Reason | Documented meaning | What to do |
|---|---|---|
| 000 | Explicit DMARC fail with p=quarantine or p=reject | Fix alignment for this sending source (sections below) |
| 001 | Implicit fail: no records, or weak policy (~all, ?all, p=none) | Publish SPF, DKIM and DMARC; Microsoft is judging without your policy |
| 010 | DMARC fail with enforcement, sender is one of your own accepted domains | Internal app or relay sending as your domain unauthenticated |
| 108 | DKIM failed due to body modification by earlier legitimate hops | Locate the gateway or list that rewrites content |
| 112 | DNS timeout prevented the DMARC record being retrieved | Check authoritative DNS health for _dmarc |
| 130 | ARC result from a trusted ARC sealer overrode the DMARC failure | Expected for configured forwarders; confirms the underlying fail |
| 905 | DMARC not enforced due to complex routing | Inbound path via on-premises or third-party gateway; fix the connector design |
The same header carries dmarc=permerror when Microsoft found a malformed DMARC record and dmarc=temperror for a temporary lookup error.
SPF failures: diagnosis and fix
SPF is evaluated against the envelope sender (MAIL FROM, also called Return-Path), not the visible From. The first question is always which domain the receiver checked, from smtp.mailfrom.
SPF fail or softfail
- Most common cause, a new or changed sending source: a SaaS platform, a new region of an email API, or an on-premises relay sends using your MAIL FROM domain from IPs not in your record. Look up the record with
dig +short TXT yourdomain.example, then compare the connecting IP from the firstReceivedheader added by the receiver. Fix by adding the provider's documented include or IP range, after confirming the source is legitimate in the Email Sending Sources Inventory Template. - Next most common, forwarding: the message passed at the first hop and failed when an alias, forwarding rule or list re-sent it from its own IP with your MAIL FROM unchanged. This cannot be fixed in your SPF record. Make sure DKIM signs with your domain so DMARC survives.
- Less common, IPv6 or a secondary egress: a sender that falls back to IPv6 or a disaster recovery relay uses an address only covered by
ip4entries.
SPF permerror
| Cause (RFC 7208) | How to confirm | Fix |
|---|---|---|
More than 10 DNS-querying terms: include, a, mx, ptr, exists and redirect count, nested includes included; ip4, ip6, all and exp do not | Expand every include recursively and count; many record checkers do this | Remove retired suppliers, replace a and mx with ip4/ip6, move suppliers to their own subdomain MAIL FROM |
| More than two void lookups (a term returning no answer or NXDOMAIN) | Resolve each included name; look for NXDOMAIN from a supplier that changed its include | Remove dead includes |
| Two or more SPF records on the same name | dig TXT returns two strings starting v=spf1 | Merge into one record; a second record often appears when a provider's setup wizard adds its own |
mx term resolving to more than 10 address records | Count the A/AAAA records behind each MX host | Replace with explicit ip4/ip6 |
Syntax error: unknown mechanism, typo such as inlcude:, stray characters | Validate the record; check copied quotes and spacing | Correct the syntax. TXT strings over 255 octets must be split; they are joined without spaces, so keep the space inside a string |
RFC 7208 also says the ptr mechanism should not be published; it is slow, costs lookups and some receivers ignore it. If your record still has it, remove it while you are there.
SPF temperror
Temperror points at your DNS, not your record: slow or failing authoritative servers, DNSSEC validation failures, or responses too large for UDP that the receiver cannot retry over TCP. Check authoritative response times from more than one network and see the DNS Investigation with dig and nslookup Guide.
DKIM failures: diagnosis and fix
RFC 6376 gives informative failure texts that many verifiers echo in logs and reports. Map the text to the cause:
| Verifier text (RFC 6376 s6.1) | Likely cause | Where to look and fix |
|---|---|---|
PERMFAIL (body hash did not verify) | Body changed after signing: disclaimer, footer, link rewriting, list footer, character set conversion | Send the same message direct to a test inbox and via each hop. If it passes direct, the hop that adds content is the cause. Sign after the last change, or have the gateway sign |
PERMFAIL (signature did not verify) | Signed headers changed (Subject tag such as [EXTERNAL] or a list prefix), or the published public key does not match the signing private key | Compare the h= list with headers modified in transit; compare the DNS key with the platform's current key after any rotation |
PERMFAIL (no key for signature) / TEMPFAIL (key unavailable) | No TXT record at <selector>._domainkey.<d=>, typo in the CNAME, or DNS not answering | dig TXT s1._domainkey.yourdomain.example; many platforms publish via CNAME, so check the chain resolves |
PERMFAIL (key revoked) | Key record has an empty p= | A rotation retired the key while the platform still signs with it; republish or switch selector |
PERMFAIL (key syntax error) | Key pasted with line breaks, quotes or truncated across TXT strings | Re-publish the key exactly as the platform provides it |
PERMFAIL (signature expired) | x= passed before verification, often on delayed or queued mail | Lengthen the signer's expiry; check queue delays |
PERMFAIL (From field not signed) | h= omits From, which RFC 6376 requires | Signer misconfiguration; fix on the platform |
dkim=policy or a receiver key-length notice | Key too short for the receiver; Google requires at least 1024-bit keys and recommends 2048 | Rotate to a 2048-bit key on a new selector |
- Canonicalisation: the RFC 6376 default is
simple/simple, which tolerates almost no change. If the signer allows it,c=relaxed/relaxedsurvives header folding and whitespace changes that intermediaries routinely make. It will not survive added content. - Do not use
l=to survive footers: RFC 6376 warns that a body length limit lets content be appended without breaking the signature, andl=0allows complete alteration of the text. - Testing flag: a key record with
t=yasks verifiers to treat failures like unsigned mail. Remove it once signing is stable, or failures will hide in plain sight.
DMARC fails while SPF or DKIM pass: alignment
This is the most common pattern in aggregate reports for organisations using third-party senders. RFC 9989 requires an authenticated identifier that aligns with the Author Domain (RFC 5322 From). In relaxed mode (adkim=r, aspf=r, the default) both must share an organisational domain; in strict mode they must be identical.
| Header evidence | Diagnosis | Fix |
|---|---|---|
spf=pass smtp.mailfrom=bounce.esp.example, dkim=pass header.d=esp.example, From is your domain | Supplier authenticates as itself. Nothing aligns | Configure the supplier's custom DKIM (your d=, usually CNAME selectors) and, where offered, a custom return-path subdomain of yours |
dkim=pass header.d=mail.yourdomain.example, From yourdomain.example, DMARC fails | Strict alignment adkim=s requires an exact match | Sign with the From domain or relax to adkim=r deliberately |
| DMARC fail on a subdomain you never use | Spoofing, governed by sp or the new np tag for non-existent subdomains | No fix needed if the policy enforces; set np=reject if not |
dmarc=permerror or no DMARC result | Two DMARC records at _dmarc (RFC 9989 discards all of them), or the first tag is not v=DMARC1 | Publish exactly one valid record |
| Policy applied one level lower than published | t=y test mode under RFC 9989 | Intended during rollout; remove when ready |
RFC 9989 (Standards Track, May 2026) obsoletes RFC 7489, removes pct, and finds the organisational domain by a DNS tree walk instead of the Public Suffix List. A receiver still on the older logic may show results that differ from one that has moved; see the DMARCbis Changes Guide. Microsoft's documentation still shows pct.quarantine and pct.reject actions for records with pct below 100.
Forwarders, mailing lists and ARC
- Symptom: aggregate reports show DMARC failures from IPs belonging to universities, forwarding services or list servers, with SPF fail and DKIM fail or pass.
- Diagnosis: if DKIM passes for your domain, DMARC passes and the SPF fail is harmless. If DKIM also fails, the forwarder modified the message (list footers and Subject tags are the usual cause).
- What you control: DKIM on every source, with relaxed canonicalisation, and short, stable headers. You cannot fix a third party's list.
- ARC: RFC 8617 is Experimental (July 2019). It records earlier authentication results in ARC-Authentication-Results, ARC-Message-Signature and ARC-Seal headers, with a chain status of
cv=none,failorpass. A receiver MAY use a trusted chain to override a DMARC failure as local policy (Microsoft reason code 130 shows it did); senders cannot require it.
Diagnosis workflow and rollback
- Capture evidence: one full failing message, its bounce text, and the last seven days of aggregate reports for the source IP.
- Identify the source: map the connecting IP to a system in your sending inventory. An unknown source is a spoofing or shadow IT finding, not a configuration fault.
- Read the three results in the Authentication-Results header and decide: mechanism failure, or alignment failure.
- Reproduce with a test send from the same platform to mailboxes at two providers, before and after the change.
- Change one record at a time, with a lower TTL set in advance so a mistake can be reverted quickly. Keep the previous record text in your change ticket.
- Roll back the policy only as a last resort: if a critical stream is being rejected and the fix needs supplier work, apply
t=yor a lower policy for a fixed period with an owner and an end date, rather than leaving the domain unprotected indefinitely. - Confirm in reports 48 to 72 hours later that the source now passes aligned.
Where identity fits
Every sending source is a non-human identity: a DKIM private key held by a supplier, an SMTP credential in an application, an email API key in a web plugin. Several failures above (keys revoked during rotation, undocumented senders, orphaned includes) are identity lifecycle failures. Give each source an owner and track its keys like any other secret; the Gravity SMTP CVE-2026-4020 incident exposed email API keys from WordPress sites, and mail sent with a stolen key authenticates perfectly.
Detection and measurement
- Aligned pass rate per source: percentage of volume from each known source passing DMARC aligned; target above 99% for every source before enforcement.
- Unknown-source volume: messages from IPs not in the inventory, trended weekly.
- SPF lookup headroom: current DNS-querying term count against the limit of 10, checked on every record change.
- DKIM key age and length: selectors older than your rotation period or under 2048 bits.
- Bounce codes: count of
5.7.515,5.7.26and DMARC-named rejections in outbound logs, alerting on any rise. - Time to diagnose: from first bounce report to identified cause.
Common failure patterns
- Fixing SPF for a DKIM problem: adding includes until the record hits permerror, when the real fault was a footer breaking the body hash.
- Two records after a supplier wizard: a second SPF or DMARC record silently voids both.
- Rotation without overlap: the old selector is emptied before the platform stops signing with it, producing
key revokedfor in-flight mail. - Trusting the wrong header: reading an Authentication-Results header from an internal hop rather than the receiver's own.
- Dropping to p=none indefinitely: an outage fix that becomes permanent and reopens the domain to spoofing.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| CIS Controls v8.1 | Safeguard 9.5 Implement DMARC | Requires DMARC with SPF and DKIM |
| NIST SP 800-53 Rev. 5 | SI-8 Spam Protection; SC-20 Secure Name/Address Resolution Service; AU-6 Audit Record Review | Receiving protections, DNS integrity, report review |
| NIST CSF 2.0 | PR.AA-03; DE.CM-09 | Authenticating services; monitoring for adverse events |
| ISO/IEC 27001:2022 | A.5.14 Information transfer; A.8.16 Monitoring activities | Protecting email transfer; monitoring reports |
| MITRE ATT&CK | T1566 Phishing; T1598 Phishing for Information | Techniques enforced DMARC makes harder with your exact domain |
Practitioner checklist
- Diagnose from the receiver's Authentication-Results header or
compauthcode, not from your own DNS view. - Decide mechanism failure or alignment failure before changing anything.
- Keep the SPF record under 10 DNS-querying terms and two void lookups, with exactly one record.
- Publish exactly one DMARC record starting
v=DMARC1. - Configure custom DKIM with your domain on every third-party sender.
- Rotate DKIM keys with overlapping selectors and 2048-bit keys.
- Find and fix the hop that modifies signed content instead of using
l=. - Lower TTLs before changes and record the previous value for rollback.
- Time-box any policy downgrade with an owner and an end date.
- Map every failing source IP to an owned sender in the inventory.
Standards and references
- IETF: RFC 9989 Domain-based Message Authentication, Reporting, and Conformance (DMARC)
- IETF: RFC 7208 Sender Policy Framework (SPF)
- IETF: RFC 6376 DomainKeys Identified Mail (DKIM) Signatures
- IETF: RFC 8601 Message Header Field for Indicating Message Authentication Status
- IETF: RFC 8617 Authenticated Received Chain (ARC) Protocol
- Microsoft Learn: Anti-spam message headers in Microsoft 365
- Microsoft Support: Fix NDR error 550 5.7.515 in Outlook.com
- Google: Email sender guidelines
Related NHI Mgmt Group resources: DMARC, SPF and DKIM Guide · DMARCbis Changes Guide · Email Authentication Checklist · Email Sending Sources Inventory Template · DNS Investigation with dig and nslookup Guide