Domain.Posture
← blog

DKIM 'body hash did not verify': what causes it and how to fix it

· dkim · email · deliverability · authentication · canonicalization

dkimemaildeliverabilityauthenticationcanonicalization

A DKIM "body hash did not verify" error means the body of the message that arrived is not the body the signer hashed. DKIM stores a hash of the body in the bh= tag of the signature; the verifier recomputes that hash over the bytes it received, and a mismatch means something rewrote the body in transit. This is almost always caused by a mailing list, forwarder, or appliance that appended a footer or disclaimer after signing — not by a broken key or a spoofer.

What the body hash actually checks

A DKIM-Signature header carries two hashes. The bh= tag is the base64 hash of the message body; the b= tag is the signature over the headers. The verifier does the body hash first: it canonicalizes the received body, hashes it, and compares to bh=. If those do not match you get "body hash did not verify" and it never even reaches the header signature.

A real signature looks like this:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=example.com; s=mail; t=1718900000;
 h=from:to:subject:date;
 bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
 b=AbCd...

That c=relaxed/relaxed is the canonicalization: the first value applies to headers, the second to the body.

The usual causes

Body-hash failures cluster around middleware that touches the body after the signer ran:

  • Mailing lists and forwarders. Mailman, Google Groups, and listservs append an unsubscribe footer or rewrite the subject with a [list-name] tag. The footer alone breaks the body hash.
  • Disclaimer and signature appenders. Corporate gateways that bolt on a legal disclaimer or "Sent from" block change the body bytes.
  • Antivirus and content rewriting. Scanners that rewrite links for click-tracking or "safe links," or normalize HTML — every byte counts.
  • The l= body-length tag. If the signer set l= to cover only the first N bytes, appended content is ignored — but many verifiers treat l= as a red flag, and a partial body is its own risk.
  • Encoding shifts in transit. A relay re-wrapping lines, changing CRLF/LF, or re-encoding quoted-printable moves the hash with no visible change.

Body hash vs header/signature failure

Do not confuse the two. "Body hash did not verify" (bh= mismatch) means the body changed — look at forwarders and appenders. A header or signature failure (the b= check) usually means a signed header was altered, the DNS public key rotated or is missing, or the selector is wrong. Different root causes, so fix the right one. Pull the published key first:

dig +short TXT mail._domainkey.example.com

If the key is present and valid but the body hash still fails, the problem is downstream of your signer.

How to fix it

  • Use relaxed/relaxed canonicalization. Simple canonicalization is intolerant of any whitespace change — a single trailing space breaks it. Relaxed tolerates whitespace normalization and is the practical default.
  • Sign after body-modifying middleware, not before. If a gateway appends disclaimers, it must sign the final outbound bytes. Reorder so the signer is the last hop to touch the body.
  • Drop the l= tag. Signing only part of the body invites both breakage and abuse. Sign the whole thing.
  • Hash exactly the bytes you send. Make sure nothing re-encodes or re-wraps the message between the signer and the SMTP edge.

For mail you forward through lists you do not control, accept that DKIM will break and lean on a sender that re-signs (ARC) plus an aligned SPF path so DMARC can still pass.

Look up your DKIM record and selector →

Further reading

Frequently asked questions

Why does DKIM body hash verification fail?
DKIM body hash fails when the message body changes between signing and verification. Mailing lists, email gateways, and appliances that append footers, disclaimers, or rewrite content after signing are the most common causes.
How do I fix DKIM body hash failures?
Use relaxed/relaxed canonicalization, ensure the signer is the last hop before SMTP, remove the l= tag, and verify no re-encoding happens between signing and delivery.
What's the difference between body hash failure and signature failure in DKIM?
Body hash failure (bh= mismatch) means the body changed in transit; signature failure (b= check) means headers were altered or the DNS key is missing or wrong. They have different root causes.