CertaDNS
Skip to lesson

Reading a Verification Result · lesson 2 of 3

body hash did not verify

After this lesson you can

Identify what modified a message from the fact that only the body hash failed.

Assumes you have read Every dkim= value and What breaks a signature in transit.

body hash did not verify is the most precise diagnostic DKIM produces. It says the message body is not byte-identical to what was signed, and it rules out every other explanation: the key was found, the signature parsed, the headers are intact.

What it rules out

  • Not a DNS problem — the key was fetched.
  • Not a key problem — the record parsed and the algorithm was supported.
  • Not a header problem — the verifier had not reached the headers yet.
  • Not a clock or expiry problem.

Something between the signer and the verifier changed the body. The remaining question is only which thing.

The usual causes, in order of likelihood

1. A footer, disclaimer or unsubscribe block appended
2. URLs rewritten for click-tracking or link protection
3. Character-set re-encoding (quoted-printable <-> base64, UTF-8 <-> ISO-8859-1)
4. Attachments stripped, rewritten or re-encoded by a scanner
5. Line endings normalised (CRLF <-> LF) by a non-conforming relay
6. The signer modifying the message after signing it

The last one is worth taking seriously when the failure is total rather than partial. If every message fails the body hash including direct delivery to a receiver with no intermediary, the problem is on your side: something in your own pipeline is signing and then editing.

Narrowing it down

  • Does it fail for every receiver, or some? Some means an intermediary on those paths. All means your own pipeline, or a change every receiver makes — which is nothing.
  • Does it fail for every message, or some? Only messages with attachments points at a scanner. Only HTML messages points at link rewriting. Only messages to one company points at their gateway.
  • Compare the delivered body with the sent body. This is the definitive step and it is usually skipped. A diff of the raw source at both ends names the culprit in seconds.
  • Check whether the path involves a list. If so, the answer is known and the fix is not on your side.

Do not reach for l=

The tempting fix for a footer is l=, which stops the signature covering anything after a given length. It also lets anyone append arbitrary content to a validly signed message of yours. A body hash that fails on a list is working correctly; the answer is to stop signing what the list is going to change, which usually means letting the list rewrite the From header and sign as itself.

Try it on a real domain

Free, no account, public DNS only.

Last reviewed