DKIM failing with "body hash did not verify"? The message body was altered somewhere between the server that signed it and the server that checked it. Your DKIM key, selector and DNS record are almost certainly correct — this is not a key problem.
The Error
In an Authentication-Results header:
Authentication-Results: mx.google.com;
dkim=fail (body hash did not verify) header.i=@example.com
header.s=selector1 header.b=Xk2mQp8v;
spf=pass (google.com: domain of bounce@example.com designates
203.0.113.10 as permitted sender) smtp.mailfrom=bounce@example.com
Or from other verifiers:
dkim=fail reason="signature verification failed" (body hash mismatch)
dkim=permerror (bad message/signature format)
The critical detail is the parenthetical: body hash did not verify. A different message — no key for signature, or signature did not verify — is a different fault with a different fix.
Why This Happens
RFC 6376 defines the bh= tag of the DKIM-Signature header as:
The hash of the canonicalized body part of the message as limited by the "l=" tag (base64; REQUIRED).
The signing server computes that hash over the body at the moment of signing. The receiving server recomputes it over the body it received. If the two differ by even one byte, verification fails. DKIM is working exactly as designed: it is telling you the message is not byte-identical to what was signed.
So the question is never "why is DKIM broken" — it is "what modified my message?" In rough order of frequency:
- A disclaimer or signature block appended after signing — most often an Exchange Online mail flow rule
- A security gateway rewriting URLs for click-time protection, or adding an external-sender banner
- A mailing list appending a footer or altering the subject
- Forwarding that re-encodes the body — changing
quoted-printabletobase64, or altering line endings - An MTA rewriting line endings between CRLF and LF, or wrapping long lines
- Antivirus or DLP products injecting a scan-result notice
Fix 1: Identify Where the Change Happens
Get the full headers of a failing message and read the Received: headers from the bottom up — the bottom is the oldest hop. Locate the hop that applied the DKIM-Signature, then examine every hop after it.
Paste the whole header block into our email header analyzer to get the hops and authentication results laid out in order rather than reading them by eye.
Then run a controlled comparison:
- Send a plain-text message with no signature block, no links and no attachments to an external mailbox you control. If that passes DKIM, the transport is clean and something about the content is triggering a modification.
- Send the same message to a second external mailbox on a different provider. Passing at one and failing at the other localises the fault to the receiving side — typically a gateway in front of that recipient.
- Send to an address on a mailing list. Failing only there confirms the list is modifying messages.
Fix 2: Find the Disclaimer Rule (Microsoft 365)
This is the leading cause inside Exchange Online tenants. Check for mail flow rules that append content:
Connect-ExchangeOnline
Get-TransportRule | Where-Object { $_.ApplyHtmlDisclaimerText -ne $null } |
Format-List Name, ApplyHtmlDisclaimerLocation, ApplyHtmlDisclaimerText
Any rule with ApplyHtmlDisclaimerLocation set to Append is adding content to the body. Whether that breaks DKIM depends on the order of operations in your specific mail flow, but if a rule modifies the body after signing, the body hash cannot survive.
Options, best first:
- Remove the disclaimer if it exists only for legacy legal habit. Many do.
- Apply it only to external recipients so internal mail is unaffected, reducing the blast radius while you work out the ordering.
- Move the disclaimer upstream so it is applied before signing rather than after.
- Add it at the client via an Outlook signature, which becomes part of the message the user composes and is therefore present before signing.
Fix 3: Check Gateways and Link Rewriting
If mail leaves your tenant through a third-party security gateway, or arrives at the recipient through one, that appliance may be modifying the body. Look for:
- URL rewriting for click-time protection, which changes every link in the body
- External sender banners injected into the body rather than added as a header
- Attachment stripping or sandboxing notices appended to the message
- Plain-text alternative regeneration in multipart messages
If the gateway sits between your signer and the internet, the correct configuration is for the gateway to sign after it finishes modifying — that is, it becomes the signer for your domain. Two systems both trying to sign the same mail is a reliable way to produce this failure.
If the gateway is on the recipient's side, you cannot fix it and you do not need to. Their receiving infrastructure broke the signature; your mail was correct when it left you. Confirm this by checking that the message passes at other providers.
Fix 4: Handle Mailing Lists Properly
Discussion lists routinely add a footer, prepend [list-name] to the subject, or strip attachments. All of these break DKIM, and there is no configuration on your side that prevents it.
The correct handling is on the list:
- The list strips the original DKIM signature and re-signs as the list's own domain, taking responsibility for the modified message.
- Or the list implements ARC (Authenticated Received Chain), which records the authentication results as they were before modification so downstream receivers can choose to trust the chain.
If you run the list, enable one of those. If you merely post to it, expect DKIM failures on list-delivered copies and make sure your DMARC policy accounts for it — this is one of the standard reasons to move to p=reject carefully rather than immediately.
What Not To Do
Do not reach for the l= tag. It limits the signature to the first N octets of the body, so appended footers no longer break the hash. RFC 6376 defines it, but the security cost is severe: everything beyond the signed length is unsigned, so an attacker who intercepts the message can append arbitrary content — new instructions, different payment details, a phishing link — and the message still passes DKIM. You would be trading a visible failure for an invisible vulnerability.
Do not disable DKIM signing to make the failure disappear. That removes the signal that survives forwarding, and DKIM is the authentication method DMARC depends on when SPF breaks.
Do not weaken DMARC to p=none as a permanent answer. It stops the symptom for spoofers as well as for you.
Verify the Fix
After removing or relocating the modification, send a fresh test to an external mailbox and read the results. In Gmail use Show original; in Outlook on the web use View message details. You want:
DKIM: 'PASS' with domain example.com
Check the DNS side is healthy too, so you can rule it out entirely:
# Microsoft 365 selectors
dig +short CNAME selector1._domainkey.example.com
dig +short CNAME selector2._domainkey.example.com
# Resolve the published key the selector points to
dig +short TXT selector1._domainkey.example.com
The selector name in DNS must match the s= tag in the DKIM-Signature header of the failing message. If it does, the key is not your problem and the body hash diagnosis stands.
Test from every sending path — mail client, application, marketing platform — because a transport rule may apply to only some of them.
Prevention
- Review mail flow rules whenever DKIM starts failing; a newly added disclaimer is the usual culprit and the change is often made by someone who did not connect it to email authentication.
- Keep exactly one system responsible for signing a given domain. Layered products each trying to sign is a recurring source of this failure.
- Publish DMARC with
rua=aggregate reporting and read it monthly. The reports show DKIM pass rates per source, so a body-hash problem appears as a trend long before anyone reports a bounce. Build the record with our DMARC generator. - When adding a security gateway, confirm during the pilot whether it modifies bodies, and settle the signing order before cutover rather than after.
- Expect list mail to fail DKIM and account for it before tightening DMARC to
reject.
Summary
- Read the parenthetical — "body hash did not verify" means the body changed, not that the key is wrong
- Trace the Received headers to find every hop after the signature was applied
- Check Exchange Online mail flow rules for appended disclaimers first
- Check gateways for URL rewriting and injected banners; ensure only one system signs
- Expect mailing lists to break it — the fix is re-signing or ARC on the list
- Never use
l=to paper over it; it makes appended content unsigned and forgeable