The DKIM Body Hash (bh=), Explained Properly

What bh= actually contains, how it's computed separately from the header signature, and why understanding the split between the two matters for real troubleshooting.

📅 Published August 2026· ⏳ 14 min read· ✍️ ToolsNovaHub Editorial Team
🛠️ Related tool: Open DKIM Lookup →

Where bh= Fits Into the Bigger Signature Picture

A DKIM-Signature header is dense with tags, and it's easy to treat bh= as just one more parameter among many without understanding the specific structural role it plays. The overall signature (the b= tag) is computed over a defined set of signed headers — and critically, the DKIM-Signature header itself, with its b= value blanked out, is one of the headers included in what gets signed. Since bh= is a tag within that same DKIM-Signature header, it's effectively signed as part of the header signature too. This is the mechanism by which body integrity gets folded into the overall signature: rather than signing the (potentially huge) body directly, DKIM hashes the body separately into a small, fixed-size digest, embeds that digest as bh= inside the header, and then signs the whole header set including that embedded digest. Tamper with the body, and the hash you'd need to recompute to match the embedded bh= value won't match, causing the overall signature check to fail.

ToolsNovaHub Pro Tip
When troubleshooting a DKIM failure, check whether it's specifically a body hash mismatch or a broader header signature failure. The two point at genuinely different root causes, and conflating them wastes time chasing the wrong half of the message.
⚠️
Common Beginner Mistake
Reaching for the l= body length tag to solve a "content gets appended after signing" problem without understanding that it deliberately creates a gap in what's actually verified — a gap that's specifically exploitable, not just an obscure edge case.

How bh= Is Actually Computed

StepWhat Happens
1Take the raw message body as it will be transmitted
2Apply body canonicalization (simple or relaxed, per the c= tag) to normalize formatting
3If an l= tag is present, truncate to only the specified byte length
4Compute a cryptographic hash (typically SHA-256) over the resulting canonicalized body
5Base64-encode the resulting hash and embed it as the bh= tag's value

A verifier performs the identical process independently on the received message's body, using the canonicalization mode and l= value (if any) declared in the signature it's checking, and compares its own computed hash against the bh= value embedded in the signature. A mismatch at this stage is specific and diagnostic: it means the body, as received, does not hash to the same value as the body that was originally signed, under the declared canonicalization rules.

Body Hash Mismatch vs. Header Signature Failure

📄
Body Hash Mismatch
The body content changed after signing, or a canonicalization mismatch between what the signer and verifier assumed occurred.
📧
Header Signature Failure (bh= matches)
Something in the signed headers changed, or the wrong public key was used for verification — the body itself is confirmed intact.
Both Fail
Often points to a more fundamental mismatch, like verifying against entirely the wrong selector's key, rather than a targeted content change.

The l= Tag: What It's For, and Why It's Risky

RFC 6376 defines an optional l= tag specifically to support scenarios where content legitimately needs to be appended to a message after it's been signed — a mailing list adding a footer, for instance. Setting l= to a specific byte count tells a verifier to compute the body hash over only that many leading bytes, ignoring anything beyond it. This does solve the specific append-after-signing problem it was designed for, but it does so by creating a deliberate blind spot: a verifier honoring l= is explicitly not checking the integrity of anything past the specified length, which means an attacker who can insert content into that unverified trailing portion can do so without breaking the signature at all. Because of this real security trade-off, using l= is widely discouraged in current guidance, and many verifiers apply extra scrutiny to signatures that use it, precisely because it's a specification feature more often associated with attempted abuse than legitimate use in current practice.

Practical Diagnosis: What a Mismatch Usually Means in the Field

In real-world troubleshooting, a body hash mismatch traces back to a small set of recurring causes: a relay or forwarding system modifying the body in a way the declared canonicalization mode doesn't tolerate (frequently simple canonicalization interacting with routine in-transit whitespace or line-ending changes), a mail client or gateway that rewrites links or inserts tracking pixels into HTML bodies after the message was already signed, or, less commonly, genuine tampering. Working through these in order — checking canonicalization mode first, since it's the most common and most benign explanation, before assuming malicious intent — tends to resolve most real cases quickly.

Walking Through a Full Verification, Body Hash Step Included

To see exactly where the body hash fits into a complete verification, it helps to walk the full sequence a verifier performs, not just the body-specific portion in isolation. First, the verifier extracts the DKIM-Signature header from the received message and parses its tags, including d=, s=, c=, h=, bh=, and b=. Second, it uses d= and s= together to construct and query the correct selector hostname in DNS, retrieving the public key. Third, it reconstructs the canonicalized form of every header listed in h= (using whichever header canonicalization mode c= specifies), plus the DKIM-Signature header itself with its b= value blanked out, exactly as the original signer would have. Fourth, and this is where bh= specifically enters the process, it independently canonicalizes the received body according to the body canonicalization mode in c=, hashes the result, and compares that computed hash against the bh= value embedded in the header set from step three. If they don't match, verification fails right there, before even reaching the final cryptographic signature check. If they do match, the verifier proceeds to the fifth step: using the retrieved public key to cryptographically verify that b= is a valid signature over the full reconstructed header set (which, remember, includes the now-confirmed-matching bh= value baked into it). Only if both the body hash comparison and the cryptographic signature check succeed does the overall DKIM verification pass.

Why Two Separate Checks Instead of One Combined Check

It's a reasonable question why DKIM bothers with a separate body-hash comparison step at all, rather than simply including the raw body directly in whatever gets cryptographically signed in one single operation. The practical answer is efficiency and flexibility. Cryptographic signature algorithms operate most efficiently over a small, fixed-size input; hashing the body down to a compact digest first, then only cryptographically signing the (also comparatively small) header set that includes that digest, avoids needing the signature algorithm itself to process a potentially large body directly. It also cleanly separates two conceptually different integrity questions — "has the body been altered" and "has the header set, including this body summary, been altered and is it authentically signed" — into two distinct, independently reasoned-about steps, which is part of why understanding them as separate failure modes, as covered above, is genuinely useful for troubleshooting rather than just a technical curiosity.

SHA-1 Versus SHA-256 for Body Hashing

RFC 6376 originally allowed SHA-1 as a hashing option alongside SHA-256, but SHA-1 has since become widely deprecated across the broader cryptographic landscape due to demonstrated practical collision vulnerabilities — meaning it's become possible, with sufficient computational effort, to construct two different inputs that hash to the same SHA-1 value, which directly undermines the integrity guarantee a hash is supposed to provide. Current guidance strongly favors SHA-256 exclusively for new DKIM deployments, and many verifying implementations treat a SHA-1-based signature with the same increased skepticism they'd apply to a 1024-bit RSA key, for closely related reasons: a hashing algorithm with known weaknesses undermines the same integrity guarantee that key length undermines when too short, just through a different mechanism.

How Body Hash Diagnostics Differ Across Common Client and Server Tools

Different tools people reach for when diagnosing a DKIM issue expose body hash information with varying degrees of directness. Command-line mail testing utilities and dedicated DKIM verification libraries typically report the computed versus expected body hash explicitly on mismatch, making the diagnosis immediate. Consumer email clients' "view original message" features generally show the raw DKIM-Signature header including the bh= value, but require manually recomputing the expected hash yourself (or using a separate tool) to actually compare against it, since the client itself usually isn't performing or reporting DKIM verification as part of that view. A dedicated DKIM lookup and verification tool bridges this gap by fetching the relevant public key, performing the canonicalization and hashing itself, and reporting a direct pass/fail specifically on the body hash component, which is considerably faster for diagnostic purposes than manually working through the raw header data.

Body Hash Behavior With Empty or Near-Empty Message Bodies

An edge case worth understanding: a message with a completely empty body (unusual, but technically valid) still produces a bh= value, since hashing an empty input is a well-defined operation for any standard hash algorithm, simply producing the hash algorithm's specific fixed digest for zero-length input. This isn't a special case DKIM needs to handle differently — the canonicalization and hashing process applies uniformly regardless of body length, including the degenerate case of no body content at all. Where this can matter practically is in troubleshooting: if a body hash mismatch occurs for what should be a body-free or near-empty message, the mismatch usually points to something being appended to a nominally empty body (an unexpected trailing newline, a signature block some system silently adds, or similar) rather than to any DKIM-specific quirk around handling empty content.

Body Hash Behavior Across Character Encodings

Message bodies can be encoded in various character sets, and canonicalization operates on the body's actual byte representation as transmitted, not on some encoding-independent abstraction of its text content. This means a message re-encoded from one character set to another in transit — even if the visible text renders identically to a human reader afterward — produces genuinely different underlying bytes, and therefore a different body hash, since canonicalization has no awareness of character encoding semantics beyond treating the body as the byte stream it actually is.

Body Hash Diagnostics as Evidence in Disputed Delivery Cases

In situations where a sender and recipient disagree about whether a message was delivered exactly as sent — a dispute over the content of a contractual notification, for instance — a body hash mismatch or match can serve as objective, verifiable evidence one way or the other, independent of either party's own recollection or claims. A matching body hash, verified against the originally signed content, is strong evidence the body reached the recipient unaltered from what was signed; a mismatch is equally strong evidence something changed somewhere along the delivery path. This isn't DKIM's primary design purpose, but it's a genuine, useful secondary benefit of having cryptographic integrity verification built into the delivery chain at all.

Related Reading in This Series

For the canonicalization rules that directly determine how the body hash is computed, see DKIM Canonicalization. For DKIM's overall signing and verification flow, read What Is DKIM. For how a stale or mismatched signature can be exploited, see DKIM Replay Attacks. To inspect a real DKIM signature and its body hash, open DKIM Lookup.

Reviewed by: ToolsNovaHub Editorial Team📅 Last updated: August 2026📜 Sourced from: RFC 7208

ToolsNovaHub tools are built and independently maintained with a focus on accurate, no-signup network and security utilities. Spotted an error? Let us know.

📋 Related Tools & Guides Comparison

ResourceTypeLink
DKIM LookupToolOpen Tool →
DKIM CanonicalizationGuideRead Guide →
What Is DKIMGuideRead Guide →
DKIM Replay AttacksGuideRead Guide →
DMARC LookupToolOpen Tool →
Try it yourself — 100% free
🚀 Open DKIM Lookup

🔗 More Guides

FAQ

A base64-encoded cryptographic hash of the message body, computed after applying whichever body canonicalization algorithm the signature's c= tag specifies, and included as part of what the overall header signature covers.
No, they're distinct but related. The bh= tag is a hash of the body, included as one value among the signed headers; the actual DKIM signature (the b= tag) is a cryptographic signature over the full set of signed headers, which includes the bh= value itself, effectively tying the body's integrity into the overall signature indirectly.
Because message bodies can be arbitrarily large, and hashing first produces a small, fixed-size representation that's efficient to include as part of the header signature, rather than requiring the full signature algorithm to process a potentially very large body directly.
SHA-256 is the current standard and overwhelmingly common choice; the older SHA-1 is deprecated and considered cryptographically weak for this purpose today.
The recomputed body hash during verification won't match the bh= value that was signed, causing the overall signature to fail verification, since the mismatch is detected as part of checking the full signed header set including bh=.
It covers the entire MIME-encoded body as transmitted, which includes attachments as part of the raw body content, not just the human-visible text portion of a message.
The optional l= tag specifies a body length limit, telling a verifier to compute the hash over only the first N bytes of the body rather than its entirety; this exists for specific append-after-signing use cases but is widely discouraged due to security risks it introduces.
Because it allows content to be appended to a message after signing without invalidating the signature, meaning a verifier using this feature is deliberately not verifying the entirety of what's actually being delivered, which attackers could exploit to append malicious content to an otherwise legitimately signed message.
Yes, this distinction matters for diagnosis: a body hash mismatch specifically indicates the body was altered after signing, while the overall header signature failing (with the body hash itself matching) indicates something in the signed headers was altered instead, or that the wrong key was used to verify.
Yes, directly — the body is canonicalized first according to whichever mode (simple or relaxed) the c= tag specifies for the body, and the hash is computed over that canonicalized result, so identical body content produces different bh= values under different canonicalization modes.
No, bh= is a mandatory tag in every valid DKIM-Signature header per RFC 6376; a signature missing it is malformed and cannot be verified as specified.
Open the message's raw source or full headers (most email clients offer a 'show original' or similar option) and locate the DKIM-Signature header; the bh= tag's value is visible directly as one of its listed parameters.
No, not necessarily — a body hash mismatch can also result from a legitimate in-transit modification that the chosen canonicalization mode wasn't designed to tolerate, such as certain forwarding or relay behaviors under simple canonicalization; it indicates the body changed after signing, not automatically that the change was malicious.
Because a body-hash-specific mismatch narrows the problem down to something about the body content or its canonicalization, letting you rule out header-signing issues, key mismatches, or selector problems, and focus troubleshooting on exactly what changed in the body and why.