Defaults.Exposed

DKIM signature not valid

“DKIM signature not valid” means the receiver found your public key, ran the verification, and the maths didn’t come out. That’s a different verdict from “no key for signature”, where the DNS (Domain Name System) lookup failed, and from “body hash did not verify”, where the body changed after signing. If you’ve checked your DNS twice and the key is right there, this verdict is telling you the key was read without trouble. The fault is in the message, in the bytes of the key itself, or in which key the signer is using.

What the verdict rules out

A DKIM-signed message carries a DKIM-Signature header with four tags that matter here: d= names the signing domain, s= names the selector, bh= is a hash of the body, and b= is the signature over that body hash plus a chosen set of headers. The receiver builds the DNS name <selector>._domainkey.<domain>, fetches the TXT (Text) record there, recomputes both hashes and checks b= against the public key. The whole procedure is RFC 6376 (Request for Comments).

Three things can go wrong in that chain, and receivers word each of them differently. If the lookup returns nothing, the key can’t be found, so the selector is wrong or missing. If the body hash the receiver computes doesn’t match bh=, something rewrote the body. If both of those succeed and the signature still doesn’t check out, you get the plain verdict this page is about.

The wording narrows the search before you’ve touched anything. A plain “signature not valid” points at the key material or the signed headers. A selector problem gives a different reason, “no key for signature”, and cause 4 below covers it in case your receiver words things differently.

Start with the Authentication-Results line

Open the full headers of a failing message and find Authentication-Results. The receiver writes its DKIM (DomainKeys Identified Mail) verdict there, and the bracketed reason is the part to read. dkim=fail (no key for signature) means the lookup at <selector>._domainkey.<domain> found no usable record, and finding the selector your signer uses is the fix.

dkim=fail (body hash did not verify) means the body changed in transit. Microsoft writes that reason as a fail, and Gmail often writes the same diagnosis as dkim=neutral (body hash did not verify). Both wordings mean the body changed, and the gateway or list that changed it is covered under DKIM.

A dkim=fail with no body-hash reason, or with a reason along the lines of “signature did not verify”, is the case for the rest of this page. Work through the five causes in the order below. The order runs from most common to least, and the first one isn’t in your DNS at all.

Cause 1: something modified the message after it was signed

A security gateway appends a disclaimer. A compliance system stamps a footer on outbound mail after the signer has already run. A mailing list adds “[listname]” to the subject line. Each of those changes either the body or a signed header, the hashes no longer match, and the signature fails even though your DNS is perfect.

The tell is the pattern of failures. Direct mail to a test mailbox passes, while mail that went through the gateway, the list or an archiving hop fails. If that’s what you see, move the signing step after the modification or stop modifying, and look again at the reason string on a message that took the long route, because it will usually name the body hash once you’re reading the right message.

Cause 2: the published key isn’t the key you generated

A 2048-bit RSA (Rivest-Shamir-Adleman) public key is longer than the 255-character limit of a single TXT string, so it has to be published as multiple quoted strings that receivers concatenate. DNS control panels handle that badly. Some truncate the paste at 255 characters and say nothing. Some split the string in the wrong place. Some inject whitespace, a stray quote or a line break into the p= value, so the record looks fine at a glance while the key inside it is rubbish.

Look the record up yourself and compare it with the key the signer generated, character for character:

dig TXT selector._domainkey.example.com

Put the p= value from the answer next to the public key from your signer’s console. If the published one is shorter, has a quote or a space in the middle, or differs at any character, re-publish it, and this time check what the control panel saved rather than what you pasted. Every receiver fails the same way on a mangled key, which is the other tell for this cause: there’s no gateway pattern, all mail fails.

Use the free scan alongside that lookup. It reports your SPF (Sender Policy Framework) record, your DMARC (Domain-based Message Authentication, Reporting and Conformance) policy and what it observes at the DKIM selectors it probes, which tells you whether SPF and DMARC still cover you while the signature is broken. It can’t see a selector it doesn’t probe, so a blank DKIM line there doesn’t mean you aren’t signing, and the dig output above stays the test for your key.

That blind spot is visible in the census itself. Of the 76,563,365 domains where the October 2026 census could run its DKIM check, 38,666,798 (50.5%) answered at a probed selector, and the check recorded no result for 255,928,954 others.

Cause 3: the signer moved to a new key and DNS didn’t follow

After a key rotation, a provider migration or an ESP (Email Service Provider) reconnect, the signer holds a new private key while DNS still serves the old public key. Each signature is then checked against a public key that no longer matches, and all of them fail.

The tell is timing: failures start on the day of the change and affect all mail from that signer. Re-copy the current record from the provider console, rather than from your own notes or a ticket from last year, and publish it under the selector the signer now uses. Never delete an old selector until traffic signed with it has drained; a queue can hold messages for days, and removing the old record early fails them on delivery.

Causes 4 and 5: the selector, then the signing settings

If the reason string was “no key for signature”, the receiver has already told you which cause you have. Confirm the s= tag in the failing message’s DKIM-Signature header, query <selector>._domainkey.<domain> with that exact selector as TXT, and publish the record the signer expects at that exact name. A record published under the wrong domain, or under _domainkey with a typo, produces the same lookup failure, and so does a selector the signer rotated away from last month.

The fifth cause is in the signer’s settings, outside DNS. Canonicalisation decides how much reformatting a signature tolerates. simple fails on changes a human can’t see: a re-wrapped line, a re-folded header, a trailing space added by a relay. Set c=relaxed/relaxed in the signer, which tolerates the whitespace re-wrapping and header re-folding that intermediate servers do routinely.

Do not use the l= body-length tag. It was meant to let footers through, and it does something worse: it lets anyone append content to a signed message without breaking the signature. It still doesn’t survive most gateway modifications either, so it adds risk without making delivery more reliable. Leave it out and let relaxed canonicalisation carry the load.

A valid signature that still fails DMARC

Once the signature verifies, check alignment. DMARC only counts a DKIM pass when the d= domain aligns with the domain in the From header. A signature that verifies as d=yourcompany.gappssmtp.com or d=yourtenant.onmicrosoft.com passes DKIM and still fails DMARC, because the signing domain belongs to the provider. That’s the usual ending for a domain whose owner switched on signing at the provider without adding their own domain, and custom-domain signing puts it right; the walkthrough is under gappssmtp signing.

Key size matters as well: use 2048-bit, not 1024-bit. 1024-bit keys sit below current cryptographic recommendations and some receivers score them down. The only cost of 2048 is the TXT splitting problem in cause 2, and CNAME (Canonical Name) delegation removes it.

Nothing rejects your mail for a broken signature on its own. Receivers treat a broken signature the way they treat a missing one. The damage lands through DMARC. When SPF doesn’t pass with alignment either, the message fails DMARC and whatever policy you published applies to it.

Turning DKIM off is the wrong escape. Since the 2024 bulk-sender rules, Gmail and Yahoo expect DKIM from volume senders, and an enforcing DMARC policy needs DKIM in practice, because SPF breaks under forwarding and DKIM is the leg that survives it.

Retire the whole class with CNAME delegation

Where your provider offers CNAME delegation, use it. Instead of pasting a public key into a TXT record, you publish a CNAME at <selector>._domainkey.<domain> pointing at a name the provider controls, and the provider serves the key from their side. The 255-character split stops being your problem, because you don’t paste the key at all. Rotation stops being your problem, because the provider changes the key at the target name and your CNAME keeps pointing at it. Causes 2 and 3 go away in one change.

Set it up once, confirm the signature verifies on a direct test message, then read the header on a message that took the gateway route. If both pass and d= matches your From domain, the signature is doing its job.

Figures from the defaults.exposed census, data as of 12 October 2026. Census numbers move every month; the current values are on the census data page.