What it means
A DKIM signature contains a hash of the message body in its bh= tag. When a receiver hashes the body it received and gets a different value, the signature can't verify: body hash did not verify. It means the body was changed somewhere between signing and delivery. The DNS key and the selector are fine; the signature was found and checked, the content just isn't the same any more.
Gmail reports it like this:
Authentication-Results: mx.google.com;
dkim=neutral (body hash did not verify) header.i=@example.com header.s=s1 header.b=AbCdEf12;
spf=pass smtp.mailfrom=example.com;
dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=example.com
Other receivers use dkim=fail with similar wording. DKIM is defined in RFC 6376.
Common causes
- Mailing lists that add a footer ("You received this because…") or an unsubscribe link to the body.
- Outbound gateways that append disclaimers, legal notices or tracking links after the message was signed.
- Security appliances that rewrite URLs for click protection, strip attachments or add banners such as "External sender".
- Encoding changes: a relay that converts 8-bit content to quoted-printable or base64, or rewraps long lines.
- Line endings or trailing whitespace changed by a server or by code that builds the message, combined with
simplebody canonicalization, which tolerates almost no change. - Signing too early: your own mail server signs, then a later step (antivirus, footer, list software) modifies the message.
How to find the cause
- Compare paths. Send the same test message directly to a Gmail or Outlook address and through the suspect route. If only one route fails, the extra hop is modifying the body.
- Read the Received headers in the failing copy, from bottom to top, to see every server it passed through. The Email Header Analyzer lists the hops in order.
- Look for visible changes: footers, banners, rewritten links.
- Check the canonicalization in the signature, the
c=tag.
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=s1;
h=from:to:subject:date:message-id; bh=...; b=...
How to fix it
- Sign last. On your own server, DKIM signing should be the final step before the message leaves, after disclaimers and footers. In Postfix with OpenDKIM as a milter, make sure any content filter that edits messages runs before the milter that signs.
- Use relaxed canonicalization:
c=relaxed/relaxed. It tolerates whitespace and line-wrapping changes. It does not tolerate added text. - Move disclaimers into the sending application or template, so they are part of the body before signing.
- Exclude signed mail from URL rewriting where your security product allows it, or have it re-sign.
- For mailing lists, you can't fix it from the sending side. Lists that change messages should rewrite the From address and sign with their own domain; many already do.
Avoid the l= (body length) tag as a workaround. It lets the signature cover only part of the body, so anyone can append content to a signed message.
Does it break DMARC?
Only if nothing else aligns. In the sample above, SPF passed for example.com and DMARC still passed. After forwarding, though, SPF fails too, and a broken DKIM signature then means dmarc=fail.
Related
- dkim=none: no signature at all.
- DMARC alignment: why one aligned pass is enough.
- DKIM record: how the public key is published.
Check your DKIM key and the rest of your setup with the Email Checker, using your own selector.