DKIM fail: "body hash did not verify" — what it means and how to fix it

This error has one meaning: the message body the receiver got is not the body that was signed. Something between the signer and the inbox changed the content. Here's how to find out what — and what to do about it.

What the error literally means

When a server signs a message, it computes a hash of the body and stores it in the bh= tag of the DKIM-Signature: header. The receiver recomputes that hash over the body it received. If the two don't match, verification stops with body hash did not verify — the signature's cryptography never even gets checked.

Two useful things follow from that:

  • It's not a DNS problem. A missing or wrong selector record produces a different failure (key not found / signature error). Body hash failures are about the content.
  • The body was modified after signing. Even one added character, an injected footer, or rewritten line endings is enough — that sensitivity is the whole point of the signature.

The usual culprits, most common first

CauseTypical signFix
Mailing list adds a footer or subject tagFails only for list trafficNothing to fix on your side — see below
A gateway appends a disclaimer or scans/rewrites contentFails for one recipient org onlySign after the disclaimer step, or stop modifying outbound mail
Forwarding service rewrites the messageFails only for forwarded copiesNothing to fix — rely on receivers honoring ARC
Your own pipeline edits mail after signing (open-tracking injection, template post-processing, CRLF normalization)Fails everywhere, consistentlyReorder the pipeline: signing must be the last step
Strict canonicalization (c=simple/simple)Intermittent fails on harmless whitespace changesSwitch to relaxed/relaxed

How to diagnose which one you have

  1. Send yourself a direct test. From the failing platform to a Gmail address you control, then open Show original. If the direct copy shows dkim=pass, your signing is fine — the failures come from something in transit (lists, forwarders, a recipient's gateway).
  2. Check who's failing in your aggregate reports. Your DMARC reports break failures down by source. A few failures scattered across many receivers usually means forwarding; consistent failure from one sending source means that source's pipeline modifies mail after signing.
  3. Look at the c= tag in a failing message's DKIM-Signature header. simple canonicalization treats trivial whitespace changes as tampering; almost every platform should be on relaxed/relaxed.

If it's mailing lists or forwarders: stop chasing it

A list that adds "[listname]" to subjects or an unsubscribe footer will break your DKIM body hash, by design — the list changed the message. You cannot fix another organization's rewriting. This is expected background noise in DMARC data:

  • A small percentage of body-hash failures on otherwise-passing mail is normal. That's one reason the rollout advice is monitor on p=none first — you learn your baseline before enforcing.
  • Modern receivers use ARC to recover the original authentication result for well-behaved forwarders, so enforcement hurts less than the raw failure count suggests.
  • What you're actually watching for is different: a new source failing consistently. That's either a misconfigured tool of yours — or someone spoofing you.

If it's your own sending: sign last

Consistent body-hash failures on direct mail mean your content changes after signing. The fix is always ordering: DKIM signing must be the final step that touches the message. Typical offenders are disclaimer appliances behind the signer, open-tracking pixel injection after templating, and relays that re-encode or normalize line endings. Move the signer to the edge, or move the modification before it.

Tired of decoding failure tables by hand? PlainDMARC reads your aggregate reports and tells you in plain English which failures are forwarding noise and which are a real sender you need to fix — one weekly verdict per domain.

Get a plain-English weekly DMARC verdict — create a free account