Tutorials11 min read

DKIM 'Body Hash Did Not Verify': Causes and Fixes

DKIM body hash failed? Something modified your email in transit. Find the culprit and fix it.

By JustEmails Platform Team

Pulled up Gmail's "Show original" on a bounce notification and stared at dkim=fail (body hash did not verify) for way too long before it clicked.

The DKIM key was fine. DNS records were correct. The selector matched. Nothing had changed on our end. But every email to that internal mailing list came back with the same failure. Took me an embarrassing amount of time to realize the problem wasn't us — it was Google Groups appending a footer and breaking the signature.

Here's the thing about "body hash did not verify": it's almost never a configuration problem on your side. Something between your mail server and the recipient modified the message body after you signed it. Super annoying. Your job is figuring out what, then deciding whether you can stop it or work around it.

We're the JustEmails team — JustEmails is built by Velocity Digital Labs. If you're tracking email campaign performance alongside deliverability, tools like JustAnalytics can help correlate authentication failures with engagement metrics. This specific error shows up less often than DKIM none (which we cover in our third-party sender DKIM guide), but when it does, it's confusing. The signature exists, the key exists, everything looks right — except the body hash doesn't match.

What You'll Learn

By the end of this, you'll be able to:

  • Read Authentication-Results headers to confirm body hash failures
  • Identify what modified your message (list servers, gateways, forwarders)
  • Fix or work around the modification
  • Avoid the l= tag trap that makes this worse

Prerequisites

  • Access to email headers (Gmail "Show original", Outlook "View source", or raw .eml files)
  • DNS access to your domain's DKIM records (for verification)
  • Basic familiarity with DKIM — if d=, s=, and bh= look like gibberish, read a DKIM intro first

How DKIM Body Hashing Works (30-Second Version)

DKIM signatures include a bh= field — the body hash. When your mail server signs an outgoing message, it:

  1. Canonicalizes the body (normalizes whitespace according to the c= tag)
  2. Hashes the canonicalized body (usually SHA-256)
  3. Base64-encodes that hash and puts it in bh=
  4. Signs everything (including bh=) with your private key

The receiving server:

  1. Fetches your public key from DNS
  2. Verifies the signature
  3. Recalculates the body hash using the same canonicalization
  4. Compares the calculated hash to bh=

If they don't match? body hash did not verify. The message body changed somewhere in transit. Simple as that.

Step 1: Confirm It's Actually a Body Hash Failure

First, check you're debugging the right problem. Open the failing message's raw headers and find the Authentication-Results header. Look for something like:

Authentication-Results: mx.google.com;
       dkim=fail (body hash did not verify) header.i=@yourdomain.com header.s=selector1;
       spf=pass ...
       dmarc=fail ...

The key phrase is body hash did not verify. That's different from:

  • dkim=fail (signature verification failed) — broken signature or wrong key
  • dkim=fail (no key) — DNS lookup failed
  • dkim=none — message wasn't signed at all

If you see anything other than "body hash did not verify," you've got a different problem. Our DKIM none guide covers the unsigned case.

Step 2: Find the DKIM Signature and Check the Body Hash

Scroll down in the raw headers until you find the DKIM-Signature header. It looks like this:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yourdomain.com;
        s=selector1; t=1720982400;
        bh=2jmj7l5rSw0yVb/vlWAYkK/YBwk=;
        h=From:To:Subject:Date:MIME-Version:Content-Type;
        b=SIGNATURE_DATA_HERE

Note the bh= value. That's what your server calculated when it signed. Now, the receiving server recalculated the body hash and got something different. The question: what changed the body?

Step 3: Identify the Modifier

This is the detective work. Common culprits:

Mailing list software. Google Groups, Mailman, Listserv, LISTSERV Maestro — they all potentially modify messages. Google Groups appends unsubscribe footers. Some Mailman installations rewrite the Subject line with [listname] prefixes. Any body modification breaks the hash.

To check: Did the message go through a mailing list? Look at the headers for List-Id, List-Unsubscribe, X-Mailing-List, or similar. If you see those, the list server touched the message.

Email security gateways. Barracuda, Proofpoint, Mimecast, Microsoft Defender for Office 365 — they rewrite URLs to route through their scanning proxies. That changes the body. Some append legal disclaimers too. (If you're running ad campaigns that link to your emails, ClickzProtect can help filter invalid clicks before they skew your analytics.)

To check: Look for headers like X-Barracuda-Spam-Status, X-MS-Exchange-Organization-SCL, X-Proofpoint-Spam-Details. If present, a gateway processed the message.

Forwarding services. If someone forwarded your email, the forwarder might have wrapped it in a new message or added content. Some email clients add "Forwarded message" headers inside the body.

To check: Look at the Received headers. Do they show hops through unexpected servers after leaving your mail infrastructure?

Your own outbound filters. This one's embarrassing but common. (Ask me how I know.) Maybe you've got an email footer tool that adds signatures after DKIM signing. Maybe a content filter rewrites certain phrases. If DKIM signing happens early in your pipeline and something modifies the body later — before the message actually leaves — you'll break your own signature.

To check: Send a test email directly (not through any intermediaries) and verify DKIM passes. Then send through your full pipeline. If direct passes but pipeline fails, something in your stack is the problem.

Step 4: Fix or Work Around

Once you've identified the modifier, your options depend on what it is:

Mailing List Footers

You can't stop list servers from modifying messages. If Google Groups appends a footer, that's Google's decision. Your options:

  1. Use a dedicated subdomain for list traffic. Send to lists from lists.yourdomain.com instead of yourdomain.com. DKIM will still fail on the list copy, but your main domain's reputation stays untouched.

  2. Configure DMARC with relaxed alignment. If your DMARC policy uses adkim=r (relaxed), subdomain signatures can still pass alignment even when the body hash fails and SPF has to carry the load. Not ideal, but better than hard failures.

  3. Hope the list server supports ARC. Authenticated Received Chain (ARC) preserves authentication results across forwarding. Some mailing list software adds ARC headers so downstream servers know the message was valid before modification. Check if your list server supports it — Mailman 3 does.

Email Security Gateways

If it's on the recipient's side, you can't control it. Some corporate gateways rewrite every URL. The recipient's admin would need to configure URL rewriting to happen before DKIM verification or preserve the original hash. Good luck getting them to do that. (I've tried. It's a lost cause.)

If it's your own outbound gateway, you have control. The fix: configure DKIM signing to happen after all body modifications. Most email security products let you order their processing pipeline. Move DKIM signing to the last step before transmission.

Example for Rspamd (which JustEmails uses): DKIM signing in Rspamd happens late by default — after content scanning. But if you've added a custom Lua script that modifies body content, make sure it runs before the DKIM module.

Your Own Pipeline

If something in your stack is modifying bodies after signing, fix the order:

  1. All body modifications (footers, signatures, link tracking, etc.)
  2. DKIM signing
  3. Transmission

Not:

  1. DKIM signing
  2. Body modifications ← this breaks everything
  3. Transmission

Audit your email pipeline. If you're using a marketing tool that adds tracking pixels or UTM parameters to links, when does that happen? Before or after the message hits your mail server for signing?

Step 5: Verify the Fix

Send test emails through the same path that was failing. Check raw headers. You want to see:

dkim=pass header.i=@yourdomain.com header.s=selector1

If it's still body hash did not verify, you haven't addressed the modifier yet. Go back to step 3.

For list server issues, the test is trickier. Send to the list, have someone on the list forward you the message (not as an attachment — inline), and check the headers on the forwarded copy. If DKIM fails on the list copy but passed on the original, your signing is correct — the list is the modifier.

The l= Tag Trap

While you're looking at DKIM signatures, check for l=. It looks like:

DKIM-Signature: v=1; a=rsa-sha256; ... l=1234; ...

The l= tag specifies "only sign the first N bytes of the body." It was designed to let you add footers without breaking signatures. Sounds helpful for exactly this problem, right?

Don't use it. Seriously. The l= tag is a security vulnerability. An attacker can intercept a message, append malicious content after the signed portion, and the signature still verifies. Phishing with your domain's valid signature. I genuinely don't understand why it's still in the spec.

RFC 6376 calls l= usage "NOT RECOMMENDED." Google ignores signatures with l= entirely for DMARC evaluation. If your mail server or email vendor is adding l= to signatures, turn it off. Sign the entire body.

(If you're curious about DKIM canonicalization settings and how c=relaxed/relaxed vs c=simple/simple affects this, that's a related topic but a different post.)

Common Errors and Fixes

Error: dkim=fail (body hash did not verify) on all emails to Google Groups

Cause: Google Groups appends unsubscribe footers and list metadata to every message.

Fix: You can't prevent this. Use a subdomain for list traffic or accept that DKIM will fail on list copies. Make sure your DMARC policy can tolerate this — p=quarantine or p=reject might cause list messages to be flagged.

Error: Body hash fails only on emails with attachments

Cause: An intermediary is modifying attachment encoding or stripping attachments entirely.

Fix: Check for gateway rules that process attachments. Some security tools re-encode Base64 attachments or remove certain file types. If it's your own gateway, configure it to run before DKIM signing.

Error: Body hash fails intermittently

Cause: Multiple mail paths. Some go through a modifier, some don't.

Fix: Trace the Received headers on failing vs. passing messages. Identify which path introduces the modification.

Error: Body hash fails after migrating to a new ESP

Cause: The new ESP's signing pipeline is different. Maybe link tracking is added after signing now.

Fix: Contact your ESP's support. Ask where DKIM signing happens in their pipeline and whether link tracking/footer insertion happens before or after.

Next Steps

If you've fixed the body hash issue, make sure your broader DKIM setup is solid. Related guides:

  • DKIM shows 'none' for third-party senders — different failure mode, similar debugging process
  • SPF permerror: too many DNS lookups — another common authentication headache
  • MTA-STS and TLS-RPT setup — the other half of email transport security

If you're managing email authentication across multiple domains, a unified dashboard helps. That's what we built JustEmails for — auto-configured SPF/DKIM/DMARC across unlimited domains, so you're not manually chasing down body hash failures on 20 different properties. For teams that need isolated browser environments to test email rendering across clients, JustBrowser provides cloud browser sessions with full dev tools.

Frequently Asked Questions

What does 'dkim=fail (body hash did not verify)' mean?

It means the email body was modified after the DKIM signature was applied. DKIM signs a hash of the message body at send time. When the receiving server recalculates the hash and it doesn't match, DKIM fails. Common culprits: mailing list footers, corporate email gateways appending disclaimers, anti-spam filters rewriting URLs, or forwarding services that modify content.

Can I prevent list servers from breaking my DKIM signature?

Not directly — the list server controls what happens after it receives your message. Some list servers preserve signatures by avoiding body modifications. Google Groups breaks signatures by default. Mailman 3 can be configured to preserve them. Your best defense: send from a subdomain dedicated to list traffic so failures don't hurt your main domain's reputation, and ensure your domain has proper DMARC policy with relaxed alignment.

What is the l= tag in DKIM and why is it dangerous?

The l= tag specifies how many bytes of the body DKIM should sign. It was designed to allow legitimate footers to be added. But attackers can append malicious content after the signed portion without breaking the signature. Most security experts recommend never using l= — sign the entire body. If you see l= in your DKIM configuration, remove it.

Why does DKIM fail when I forward email?

Forwarding services often modify messages — adding headers, wrapping content, rewriting links for tracking, or appending footers. Any body change after the original signature invalidates DKIM. ARC (Authenticated Received Chain) exists to preserve authentication across forwards, but not all forwarders implement it. If you're forwarding to a mailing list or corporate system that modifies messages, expect DKIM to fail on the forwarded copy.


Try JustEmails

Unlimited custom domain email hosting for $49/year flat — unlimited domains, unlimited mailboxes, 10 GB storage, full IMAP/SMTP. Built for agencies, freelancers, and anyone managing email across more than one domain.

Start your 7-day free trial → · How it compares

dkimemail-authenticationbody-hashdeliverabilitydns-recordsbuildinpublicsaasstudioaiworkforcebuildwithclaude

Related posts