Wednesday, 28 October 2026
Email/Vox
Authentication

DKIM’s simplest check: hash an empty body and compare

A simple, reproducible test proves your DKIM signer isn’t mangling whitespace.

Nora Vale

By Nora Vale· Email Vox · 28 Oct 2026

A red wax seal centered on a blank sheet of paper, symbolizing a fixed message body before signature.

Key facts

  • ■Word to the Wise shows a reproducible DKIM bh= test using an empty email body.
  • ■Simple canonicalization yields a CRLF-only body; relaxed yields a zero-byte body.
  • ■The expected empty-body hashes are published for simple and relaxed in RFC 6376.
  • ■CRLF handling differences commonly break DKIM body hashes, per Word to the Wise.
  • ■bh= appears in DKIM-Signature alongside a=, c=, d=, s=, t=, h=, and b= fields.

DKIM looks like a black box until you crack open one tiny part: the body hash. Word to the Wise just demoed a lab‑friendly way to see it, compute it, and sanity‑check your signer with an empty message body.

The post walks through RFC 6376’s body canonicalization, the hashing step using SHA‑256, and the exact base64 bh= values you should see for empty bodies under “simple” and “relaxed.” It also shows where that bh= lands in a real DKIM‑Signature header you can examine in a mailbox like Gmail.

What happened

Word to the Wise explains that DKIM signing starts by canonicalizing the body, then hashing it with SHA‑256 and base64‑encoding the result for the bh= tag in DKIM‑Signature. The piece keeps the scope on the body, not headers, to make the process testable.

The post defines two body canonicalizations from RFC 6376: simple and relaxed. Simple removes trailing blank lines and ensures the body ends with a CRLF; relaxed removes trailing spaces on each line, collapses runs of internal whitespace to one space, and also drops trailing blank lines.

CRLF—carriage return + line feed—is the two‑byte end‑of‑line marker in email, often typed as “\r\n.” The post explicitly notes the bytes as 10 and 13, matching how the sequence is discussed in its walkthrough.

For an empty email body, simple canonicalization reduces it to only a CRLF, making the canonicalized body exactly two bytes long. Under relaxed canonicalization, the same empty body canonicalizes down to an empty string of zero bytes.

To compute the bh=, the post uses the SHA‑256 algorithm on the canonicalized body, then base64‑encodes the binary digest. The example commands are straight from a shell pipeline: echo -n of the canonicalized body, piped to openssl sha256 -binary, then into base64 for the final string.

For the simple canonicalization of an empty body, the expected base64 hash is frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN/XKdLCPjaYaY=. That exact value is what you should see in bh= when your signer uses c=relaxed/simple or c=simple/simple for the body piece.

For the relaxed canonicalization of an empty body, the expected base64 hash is 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=. That’s the canonical relaxed result for an empty body and is independently listed in RFC 6376 for verification.

The post shows how this plays out in a real header. In Gmail or another mailbox, you’ll see a DKIM‑Signature with a=rsa-sha256 confirming SHA‑256 is in use, c=relaxed/simple naming header/body canonicalization, and bh= with the simple empty‑body hash above.

The example DKIM‑Signature includes other parameters you can use for diagnostics: d=blighty.com as the signing domain, s=blueberry as the selector, t=1793116010 as a signing timestamp, a header list in h=, and the b= block that holds the RSA signature itself.

Word to the Wise calls this “really, that’s how it works,” because you can confirm the numbers by sending yourself an email with an empty body and inspecting the DKIM‑Signature headers end‑to‑end. It’s a reproducible test you can run on any signer you control.

The write‑up emphasizes that body canonicalization bugs are a common failure point in DKIM implementations, particularly differences in how trailing CRLFs are handled between simple and relaxed. Those differences can tip a valid signature into failure at the receiver.

The post suggests using the empty‑body case as a smoke test for any newly written or freshly configured DKIM signer. If the bh= doesn’t match the published values for your c= mode, fix the canonicalization before chasing keys, DNS, or library issues.

The background

DomainKeys Identified Mail (DKIM) binds a message to a sending domain by signing selected headers and the canonicalized body with a private key; receivers verify with a public key in DNS. The bh= is the anchor that proves “this is the body we signed.”

RFC 6376 details canonicalization options to keep small transit changes from breaking the signature. Simple keeps line endings strict; relaxed trims and normalizes whitespace to absorb minor formatting shifts without changing the verified hash.

The hashing step in the post uses SHA‑256, which is stated in the DKIM‑Signature via a=rsa-sha256. That pairing is the standard choice in modern deployments and is what you’ll see on the wire when inspecting example headers.

In practice, many delivery headaches trace back to body mangling or newline quirks rather than DNS. Word to the Wise argues the best way to isolate that is to control the content: start with an empty body whose expected bh= is published and unambiguous.

If you’re building a broader authentication plan, our earlier guide on a 4‑week DMARC hardening plan shows how DKIM alignment fits into enforcement. For even more mailbox‑side context, the authentication hub collects recent changes that affect verification and deliverability.

What people are saying

“Checking that the body hash of an empty email is correct is a good smoke test,” Word to the Wise writes, stressing that implementers often stumble on trailing CRLF handling rather than on DNS or RSA.

The post’s practical recipe—empty body, known c=, reproducible bh=, visible DKIM‑Signature—reflects a testable approach that teams can adopt quickly without deep cryptography. It relies on shell tools many ops teams already use: echo, openssl, and base64.

The example header values (d=blighty.com, s=blueberry, t=1793116010) underscore how concrete data in DKIM‑Signature aids debugging. When comparing bh= across hops, those fields help confirm which signer and configuration produced the message on a given path.

While the post doesn’t cover DMARC directly, its advice reduces DMARC misalignment driven by broken DKIM. Our reporting on Stripe requiring DMARC for custom domains highlights why keeping DKIM healthy now feeds bigger policy requirements upstream.

For senders wrangling infrastructure drift, our explainer on locking down email in 30 days pairs well with this smoke test. A pinned bh= check can catch library upgrades or MTA tweaks that silently change canonicalization.

What happens next

If your bh= doesn’t match the expected simple or relaxed empty‑body value, the next step is to audit your signer’s canonicalization code and configuration. The post specifically flags trailing CRLF handling as a likely culprit and a frequent source of regressions.

Teams can build a small CI job that feeds known inputs—empty body under each c= variant—through their signer and asserts the resulting bh=. That turns a blog demo into a guardrail that protects deliverability as dependencies change.

For operational docs, include the exact commands from the post in your runbooks: echo -n of the canonicalized body into openssl sha256 -binary, then base64. On‑call responders can repeat that locally and compare bh= in production headers within minutes.

If you need to demonstrate end‑to‑end behavior, the post suggests sending yourself a blank‑body email and reading the full headers in a mailbox like Gmail. Seeing a=rsa-sha256, c=relaxed/simple, and the expected bh= confirms your signer’s behavior matches the RFC‑described flow.

Finally, treat the empty‑body case as a baseline. Once it passes, extend tests to bodies with trailing spaces, multiple internal spaces, and deliberate blank line tails to validate the relaxed rules. That builds confidence before moving on to header canonicalization and full DKIM coverage.

The Email Vox take

  • Reproduce the empty‑body test in staging. Send a blank‑body email signed with your current c= setting and confirm the bh= matches the expected simple or relaxed value.

  • Inspect your signer’s canonicalization defaults. If you rely on c=relaxed/simple, validate that newline trimming and whitespace collapsing behave per RFC 6376. Lock those settings in config management and unit tests.

  • Add an automated “empty body” CI check. Pipe known inputs through your signing stack and assert the bh= against the published values. This catches regressions when libraries or MTAs change how they handle CRLFs.

  • Document how you compute bh=. Include the hashing command, encoding step, and examples in your runbooks so on‑call can quickly isolate c= and bh= issues during delivery incidents.

Frequently asked questions

what does dkim body canonicalization actually change?
Per RFC 6376, simple removes trailing blank lines and ensures a CRLF at the end; relaxed trims end-of-line spaces, collapses internal whitespace, and removes trailing blank lines.
what is crlf in email?
CRLF is the two‑byte line ending for email: carriage return + line feed, written “\r\n.” The source notes the bytes as 10 and 13.
what’s the dkim body hash for an empty email body?
For simple canonicalization it’s frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN/XKdLCPjaYaY=. For relaxed canonicalization it’s 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=.
where does the body hash show up in an email?
In the DKIM-Signature header’s bh= field, alongside parameters like a=, c=, d=, s=, t=, h=, and the b= signature block.
how do i reproduce the dkim body hash locally?
Hash the canonicalized body with SHA‑256, then base64 it. Example for simple empty body: echo -n "\r\n" | openssl sha256 -binary | base64
what’s a quick dkim smoke test i can run?
Send an email with an empty body, then confirm the bh= value matches the expected hash for your chosen body canonicalization (simple or relaxed).
does dkim always use sha-256?
The example shows a=rsa-sha256 in DKIM-Signature, and the hashing step described uses SHA‑256 as defined in RFC 6376.
why do some dkim checks fail on otherwise fine mail?
Word to the Wise says body canonicalization bugs—especially handling of trailing CRLFs—are a common cause of signing or verification mismatches.

Why this matters

Why this matters A lot of DKIM troubleshooting gets lost in the cryptographic weeds, when whitespace is the real culprit. Word to the Wise’s walk‑through shows how to validate the body hash in a way any team can reproduce. When the bh= matches known values for empty bodies, you know your canonicalization and hashing are sound—before spending hours chasing keys or DNS. That helps prevent silent breaks in authentication that undermine DMARC alignment, delivery, and phishing defenses across your ecosystem.

Sources

Reporting draws on the public sources listed above. Spotted an error? Request a correction.

Related coverage