Error message
dkim=fail reason="signature verification failed"
Also printed as
- dkim=fail (bad signature)
OpenDKIM writes `dkim=fail reason="signature verification failed"` for both header and body changes (reproduced with OpenDKIM 2.11.0). mailauth distinguishes them: "bad signature" for headers, "body hash did not verify" for the body.
What it means
The receiving server found the DKIM key and computed the signature over the message as it arrived, and it did not match: the signed headers (or, for OpenDKIM, also the body) changed after the sender signed the message, or the published key does not belong to the signing key. DKIM signs exact bytes after canonicalization, so any rewrite by a relay — a subject tag, a footer, a re-encoded MIME part — breaks it. For DMARC this counts as a failed DKIM check.
Why it happens
A relay changed a signed header
commonMailing lists that tag the Subject (`[team]`), gateways that rewrite `From` or `To`, or security appliances that change `Content-Type`. Every header listed in `h=` is covered.
Footers, disclaimers or re-encoding
commonAppended disclaimers, link rewriting, antivirus banners or converting 8-bit content to quoted-printable alter the body. With `l=` absent, every byte counts.
The DNS key does not match the signing key
occasionalAfter a key rotation the selector's TXT record still holds the old public key, or two servers sign with the same selector but different private keys.
The signer modified the message after signing
occasionalSigning before a content filter adds headers or rewrites the message (milter order in Postfix), or a sender library that signs and then changes line endings.
How to fix it
- 1.
Compare the signed headers with the received ones
The `h=` tag lists what was signed. Compare those headers in the received copy with a copy taken at the sender (Sent folder).
bash # Which headers did the signature cover, and what do they look like now? grep -i "^DKIM-Signature" -A4 received.eml | grep -o "h=[^;]*" grep -iE "^(From|To|Subject|Date|Message-ID|Content-Type):" received.eml - 2.
For lists and forwarders: ARC and re-signing
Intermediaries that must modify messages should add ARC headers and sign with their own domain, so receivers can still evaluate the original authentication.
text # Mailing list / forwarder: add ARC and re-sign with the list's own domain (Mailman 3 example) [ARC] enabled: yes authserv_id: lists.example.org domain: lists.example.org selector: arc2026 privkey: /etc/mailman3/arc.private - 3.
Verify the DNS key against the private key
`opendkim-testkey` confirms that the published record for the selector matches the key the signer uses.
bash opendkim-testkey -d example.com -s sel -k /etc/opendkim/keys/sel.private -vvv # DNS record matches the private key?