Learn · Fixing a signature
dkim=fail: what the reason string tells you
dkim=fail is not one failure with one fix. It is the headline on at least five different problems, and the receiver almost always tells you which one — in a reason string most people scroll straight past. Read it first. The difference between "the message was modified in transit" and "your key is wrong" is the difference between a change you cannot make and a change you can, and they look identical until you look.
If you are diagnosing your own domain rather than a message you received, start with the free DKIM checker: it fetches the key at a selector you name and tells you whether what is published is usable at all.
Find the reason first
Every result lives in the Authentication-Results header the receiving server adds. In Gmail, open the message, choose "Show original"; in Outlook, "View message source". Look for the line that names the receiving domain:
Authentication-Results: mx.google.com;
dkim=fail reason="body hash did not verify"
header.i=@yourdomain.com header.s=selector1;
spf=pass smtp.mailfrom=yourdomain.com;
dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=yourdomain.comThree things on that line matter. header.s= is the selector, which tells you which key was used — useful when a platform rotates keys or you run several senders. header.i= or header.d= is the signing domain, which is what DMARC alignment is judged against. And reason= is the one that picks your fix.
| Result | What the receiver is saying | Where the fix is |
|---|---|---|
fail — body hash | The message changed after you signed it | Usually not yours to fix |
fail — signature | The key does not match the signature | Your DNS or your platform |
neutral | The signature could not be evaluated | How the record was published |
permerror | Something is malformed beyond evaluating | Your record or your signer |
temperror | The key could not be fetched just now | Nowhere — wait |
"body hash did not verify"
This is the most common dkim=fail and the one people most often misdiagnose, because the instinct is to suspect the key. The key is fine. A DKIM signature contains a hash of the message body taken at signing time; the receiver recomputes that hash on what actually arrived and compares. A mismatch means the body changed on the way.
Something in the path rewrote it. The usual suspects, in rough order of how often they turn up:
- A mailing list. Appending an unsubscribe footer is enough. Most lists also prefix the subject line, which breaks the signature a second way because
Subjectis normally signed too. - A security gateway or appliance. Disclaimers added on the way out, "external sender" banners added on the way in, and link rewriting for click protection all modify the body after signing.
- Your own mail server adding a footer downstream of whatever applied the signature. Worth checking when the failure is consistent and only your domain is affected.
- Encoding changes. A relay that re-encodes the body, or converts line endings, changes the bytes even though the message looks identical.
When the modification happens outside your control — which is most of the time — there is no fix at your end, and that is the point of ARC: it lets a forwarder vouch that authentication passed before it made its change. What you can do is make sure DKIM is not carrying your DMARC alignment alone, because a domain relying on DKIM only will fail DMARC every time a list touches a message.
One thing not to do: the l= tag limits how much of the body the signature covers, which makes appended footers stop breaking it. It also lets anyone append arbitrary content to your signed message and have it still verify. Do not use it to make this error go away.
"signature verification failed" and bad signatures
Here the body arrived intact and the cryptography still did not check out, which points at the key rather than the message. Four causes cover nearly all of it:
- The key was rotated and DNS was not updated — or was updated, and receivers are still holding the old record until its TTL expires. A rotation that looks instant at the platform is not instant at the edge.
- The selector does not match. The signature says
s=selector1and your DNS publishesselector2._domainkey. Common after migrating platforms, where the old selector is deleted before the new one is fully live. - The published record is truncated. A single DNS string maxes out at 255 characters and a 2048-bit key is longer than that, so it has to be split into several quoted strings inside one TXT record. Some DNS control panels split it correctly, some truncate it silently, and some insert spaces that are not supposed to be there.
- A header was modified in transit — most often the subject line, prefixed by a list. This shows as a signature failure rather than a body hash failure, and it has the same non-fix as the section above.
Test what is actually published rather than what you pasted: the DKIM checker resolves the selector and reports whether the key parses and what size it is. A record that comes back short is the truncation case.
dkim=neutral and dkim=permerror
These two mean the receiver never got as far as a verdict. neutral is "there is a signature and I could not evaluate it"; permerror is "something here is malformed and no retry will help". In practice both point at the same short list:
- No TXT record at
selector._domainkey.yourdomain.comat all — the platform is signing with a selector nobody published. - A record that exists but is not a DKIM key: an empty
p=tag (which is how a key is formally revoked), a missingv=DKIM1, or stray quoting from a copy-paste. - A CNAME at the selector pointing somewhere that no longer resolves, which is how most hosted platforms publish keys and how they break when a subscription lapses.
Unlike a body hash mismatch, this one is entirely yours and entirely fixable. Confirm the selector from header.s= in the failing message, then check that exact name.
dkim=temperror
The receiver tried to fetch your public key and could not — a DNS timeout, a lookup failure, a nameserver that was briefly unreachable. It is temporary by definition and changing your configuration in response is how a working setup gets broken at two in the morning.
A single temperror is noise. A steady trickle across providers is a signal, and it is about your DNS hosting rather than DKIM: check that every nameserver for the zone answers for _domainkey names, and that nothing is rate-limiting queries. Watch for it in your aggregate reports rather than in one message.
When DKIM passes and DMARC still fails
Worth knowing before you spend an afternoon on a signature that was never the problem. DKIM verifying is not the same as DKIM aligning. DMARC compares the signing domain in d= against the domain in your From header, and a platform that signs with its own domain produces a message that is genuinely authentic and aligns with nothing:
dkim=pass header.d=sendingplatform.com
From: you@yourdomain.com → dkim aligned: noThe fix is a signing domain of yours, which nearly every platform supports and calls custom or branded DKIM. That case has its own page, because the symptom — green checks and a red DMARC result — sends people looking in the wrong place: DMARC fails but SPF and DKIM pass. If instead both checks are red in your reports, the causes are different again and SPF and DKIM both showing fail covers them.
For how signing works underneath any of this — selectors, what gets hashed, why forwarding is survivable at all — see what DKIM is.
Free checks: DKIM checker DMARC checker SPF checker Report analyzer