DKIM

DKIM Fail (Signature Did Not Verify)

"DKIM signature did not verify" means the b= signature check failed — a DNS public-key mismatch or a signed header altered after signing. Here's how to fix it.

Updated Jul 1, 2026

The short answer

"DKIM fail (signature did not verify)" means the receiving server recomputed the cryptographic signature in the DKIM-Signature b= tag and it did not match — the email's signed headers were altered after signing, or the public key in DNS no longer matches the private key that signed the message. Fix it by republishing the correct selector key in DNS, rotating keys in the right order, and stopping any gateway that rewrites signed headers.

"Signature did not verify" is the result a DKIM verifier reports when the cryptographic check of the b= tag in the DKIM-Signature header fails. It is not the same as "body hash did not verify" — that one means the message body changed. This one means the signed headers or the key itself don't line up.

What does "signature did not verify" actually mean?

When you send mail, your signer hashes a specific set of header fields (the ones listed in the h= tag) plus the DKIM-Signature header itself (with b= treated as empty), then signs that hash with your private key. The receiver fetches your public key from selector._domainkey.yourdomain in DNS and verifies the signature (RFC 6376 §6.1.3, "Compute the Verification").

If that verification fails, it almost always means one of two things happened:

  1. The keys no longer match. The public key published in DNS is not the mathematical pair of the private key that signed the message.
  2. A signed header was modified in transit after your server signed it.

This is distinct from the body-hash (bh=) check, which fails when footers, disclaimers, or re-encoding alter the body. If your report says "body hash did not verify," that's a different problem.

How do I fix "DKIM signature did not verify"?

Work through these in order:

1. Confirm the published key matches the signing key. This is the most common cause. Pull the public key the receiver sees:

dig +short txt selector._domainkey.yourdomain.com

Replace selector with the value from the s= tag in the failing message's DKIM-Signature header. Verify the p= value is the public key that pairs with the private key your sending platform is using. If you regenerated or rotated keys (or migrated ESPs) without updating DNS, the old/wrong key here is the failure.

2. Check for a truncated key. A 2048-bit key exceeds the 255-character limit for a single DNS TXT string and must be split into multiple quoted strings within one TXT record. A record cut off at 255 chars yields an unusable key and a failed signature. Re-publish the full key as concatenated quoted strings, exactly as your provider gives it.

3. Rotate keys in the right order. RFC 6376 §5.2 gives explicit operational advice on this: publish the new public key in DNS, then switch the signer to the new private key right away — don't wait for propagation. Keep the OLD public key published for a reasonable overlap window afterward, so mail already in transit (signed with the old key) can still be verified by receivers. Removing the old key too soon, before in-flight mail has cleared, is what actually creates a verification gap.

4. Stop anything that rewrites signed headers. Unlike body-hash failures, header tampering breaks the signature too. A secure email gateway, mailing list (Subject prefixing/From: rewriting), or appliance that edits a header named in your h= tag — most often Subject or From — will invalidate the signature. Either exclude those systems from the signed path, sign after they run, or remove volatile headers from h=.

5. Use relaxed header canonicalization. RFC 6376 defines two header canonicalization algorithms: simple (§3.4.1) tolerates no changes at all — not even folding whitespace — while relaxed (§3.4.2) survives benign reformatting by relays, such as case changes to header names and whitespace collapsing. Sign with c=relaxed/relaxed for resilience.

6. Verify the selector resolves at all. If selector._domainkey returns NXDOMAIN, RFC 6376 §6.1.2 defines this as PERMFAIL (no key for signature) — a distinct result from the b= mismatch this page is about, though most DMARC/mail-client reports collapse all of these into a generic dkim=fail. An empty p= is separately defined as PERMFAIL (key revoked). Either way, confirm the record exists and isn't behind a wildcard or CNAME that drops the TXT.

With Courier

If you send email through Courier, DKIM is handled by your configured email provider (e.g. SendGrid, Amazon SES, Postmark). Generate the DKIM key in that provider's dashboard, publish the exact CNAME/TXT records they give you on your sending domain, and confirm the provider shows the domain as "verified" before sending. A "signature did not verify" result usually traces back to those DNS records being incomplete, truncated, or out of date relative to the provider's current key.

After any change, send a test to a mailbox you control and inspect the Authentication-Results header for dkim=pass.

FAQ

Common questions

'Body hash did not verify' (bh= tag) means the message body was altered after signing — typically a footer, disclaimer, or re-encoding added in transit. 'Signature did not verify' (b= tag) means the overall cryptographic signature failed: either the DNS public key doesn't match the private signing key, or a signed header (like From or Subject) was modified after signing.

One API, every provider

Stop debugging raw provider errors

Courier connects to your email, SMS, and push providers, handles retries and failover, and surfaces delivery errors in plain language.

Reply-code definitions per RFC 6376 §6.1.3. Last reviewed Jul 1, 2026. Courier is not affiliated with third-party providers; error behavior may vary by implementation.