DKIM Failures Explained: The Receiver-Side Verdict Catalogue (RFC 6376 §3.9 + RFC 8601 §2.7.1 — dkim=none / neutral / pass / fail / policy / temperror / permerror — Authentication-Results Anatomy, DMARC Alignment Bridge, and the Per-Verdict Diagnostic Decision Matrix)

The receiver-side verdict catalogue for DKIM. Documents each of the seven canonical Authentication-Results verdicts that an RFC 8601 §2.7.1 verifier emits for a DKIM-Signature header per RFC 6376 §3.9: none, neutral, pass, fail, policy, temperror, permerror. Pairs each verdict with its structural meaning (what state the verifier is reporting), common causes (what produces it in practice), operational consequence (whether DMARC alignment is impacted), and diagnostic next step (the action required to resolve it). Includes the Authentication-Results anatomy reference, the DMARC alignment bridge (why dkim=pass does NOT mean DMARC passes), the DMARC rua <auth_results><dkim> block format reference, and a per-verdict diagnostic decision matrix. Differentiation from sibling guides: help_dkim_explained.php is the sender-side protocol primer; help_dkim_canonicalization.php is the c=tag deep dive; help_dkim_for_microsoft_365.php and help_dkim_for_google_workspace.php are per-provider deployment guides; this page is the verdict-decode reference for operators arriving from a DMARC aggregate report row.

This reference is provided in English. RFC 6376 §3.9 status keywords (none, neutral, pass, fail, policy, temperror, permerror) and Authentication-Results syntax (dkim=X header.d= header.s= header.b= reason="..." comment per RFC 8601 §2.7.1) do not translate. We keep the body in the source language so technical terms remain consistent with IETF RFC documentation.

TL;DR

  • Seven canonical DKIM verdicts per RFC 6376 §3.9 + RFC 8601 §2.7.1: none, neutral, pass, fail, policy, temperror, permerror. Each one tells you a structurally different thing about the message under verification.
  • none vs fail vs permerror are the three most-confused verdicts. none = no signature present at all. fail = signature present and evaluated, but cryptography failed. permerror = signature present but could not be evaluated (missing public key, malformed signature, unsupported algorithm).
  • dkim=pass does NOT mean DMARC passes. DMARC additionally requires ALIGNMENT — the d= tag must match (strict) or share an organizational domain with (relaxed) the From: header per RFC 7489 §3.1. The M365 onmicrosoft.com and Workspace gappssmtp.com gotchas are pure alignment failures with cryptographic dkim=pass.
  • temperror is retryable; permerror is not. Concentrated temperror clusters from one receiver = transient DNS issue at THAT receiver; isolated temperror across receivers = your authoritative nameserver is flaky.
  • policy is a receiver-side rejection distinct from fail — common causes are a key the receiver considers too weak (RFC 8301 permits RSA from 1024 bits and recommends 2048), the rsa-sha1 algorithm (forbidden by RFC 8301; also reported as permerror), a signature the receiver considers too old (t=; a signature past its x= is a permerror under RFC 6376), or headers the receiver's policy requires missing from the h= list.
  • fail is almost always a forwarder/list problem when the underlying signature was valid at origin. Mailing-list footer injection, MTA body re-encoding, Microsoft Defender anti-spoof rewrite, security-gateway sandboxing — all of these break the body hash. ARC sealing (RFC 8617) is the protocol-level fix.
  • Per-verdict diagnostic-next-step matrix is the section below. Map the verdict to the action and execute.
  • DKIM failures arrive most often through DMARC aggregate reports — DMARC Analyzer's reports view (parsed via includes/DmarcParser.php) breaks down DKIM verdicts per reporter and per source IP so you can localize a failure to a single receiver, source, or time window.

What this guide covers

A complete receiver-side verdict reference for DKIM. Where DKIM explained documents how the signing pipeline works (sender-side: key generation, header canonicalization, body-hash computation, DKIM-Signature header emission per RFC 6376) and DKIM canonicalization documents the c= tag's simple-vs-relaxed tradeoff, THIS page documents the receiver-side verdict the verifier emits after evaluating the signature - what each verdict means structurally, what it implies operationally, and what diagnostic action it calls for.

The seven verdict keywords are normative per RFC 8601 §2.7.1 (Message Header Field for Indicating Message Authentication Status); they map back to the RFC 6376 §3.9 status enumeration that DKIM verifiers internally produce. Every DMARC aggregate report (RFC 7489 §7.2) that your rua= mailbox receives contains a <auth_results><dkim> block whose <result> element uses exactly these seven keywords.

The page is anchored against the Feb 2024 Google/Yahoo bulk-sender enforcement that demands aligned DKIM for high-volume senders. Misdiagnosing dkim=fail as "DKIM broken" when the actual cause is a forwarder body rewrite leads to wasted hours on the wrong fix; this page exists to short-circuit that misdiagnosis.

Anatomy of a DKIM verdict in Authentication-Results

The verifier records its verdict on each message in an Authentication-Results header per RFC 8601. A representative DKIM-only stanza:

Authentication-Results: mx.google.com;
       dkim=pass header.i=@example.com header.s=selector1 header.b=AbCdEf12;
       dkim=fail (body hash did not verify) header.i=@list.example.org header.s=2024a header.b=ZyXwVu90;

A single message MAY produce MULTIPLE DKIM verdicts when it carries multiple DKIM-Signature headers - each signer's signature is evaluated independently and emits a separate dkim= stanza. The example above shows the sender's original signature passing (dkim=pass header.d=example.com) and a mailing-list re-signature failing (dkim=fail header.d=list.example.org, body rewritten by list footer injection).

Per-stanza fields

  • dkim=<verdict> — one of the seven RFC 6376 §3.9 / RFC 8601 §2.7.1 keywords. The verdict.
  • (<optional reason>) — optional free-text describing the failure cause (e.g., "body hash did not verify", "key not found", "weak key size"). Receiver-specific format; not normative.
  • header.i=<AUID> — the agent or user identifier from the i= tag of the DKIM-Signature header (typically @<signing-domain>).
  • header.d=<domain> — the signing-domain identifier from the d= tag. THIS is the value DMARC alignment checks against the From: header.
  • header.s=<selector> — the selector from the s= tag. Locates the public-key TXT at <s>._domainkey.<d>.
  • header.b=<sig-prefix> — at least the first eight characters of the b= tag value (RFC 6008). Useful for cross-referencing the verdict with the actual signature header without quoting the full signature.

Multiple Authentication-Results headers

A message that traverses multiple authentication-aware MTAs may accumulate multiple Authentication-Results headers, each scoped to the authserv-id at the start of the header (mx.google.com in the example). When debugging always identify the AUTHORITATIVE Authentication-Results — the one whose authserv-id matches the receiving MTA that ultimately delivered or rejected the message.

Verdict: dkim=none

Definition: per RFC 8601 §2.7.1, the message was not signed — the verifier had nothing to evaluate.

What it means

The message arrived without a single DKIM-Signature header. This is different from dkim=fail (signature present, crypto failed) or dkim=permerror (signature present but unevaluable). The verifier did not run cryptography at all because there was no input.

Common causes

  • DKIM was never enabled at the sending platform. The #1 cause. On M365 the Set-DkimSigningConfig -Enabled $true step was skipped; on Workspace the Start authentication click never landed.
  • The sender is mailing through a non-DKIM-enabled path — a bulk-mail platform that does not sign on the sender's behalf, an ESP whose free-tier account does not include DKIM signing, an SMTP-relay device that strips DKIM by default.
  • An MTA in the path stripped the DKIM-Signature header. Rare in 2024 but possible with legacy appliances or misconfigured Default-Frontend Connectors on hybrid Exchange (see DMARC for Hybrid Exchange).
  • The signing domain's outbound relay was bypassed — direct SMTP submission to the receiver that bypasses the platform's signing layer. Common with on-prem connectors and misconfigured SMTP-AUTH endpoints.

Operational consequence

DMARC requires at least one of DKIM or SPF to PASS AND ALIGN. dkim=none provides neither pass nor alignment (no signing domain to align against). If SPF also fails or misaligns, DMARC fails. dkim=none is the most-common DMARC failure root cause for senders that never finished DKIM rollout.

Diagnostic next step

Verify DKIM is enabled and active at the sending platform:

  • For Microsoft 365: Get-DkimSigningConfig -Identity <your-domain> | Format-List Enabled,Status must return Enabled=True; Status=Valid. See DKIM for Microsoft 365.
  • For Google Workspace: Admin Console Authenticate email panel must show domain status Authenticating email. See DKIM for Google Workspace.
  • For any platform: send a test message to a mailbox you control and inspect the headers. If no DKIM-Signature appears, signing is not happening on that path.

Verdict: dkim=neutral

Definition: per RFC 8601 §2.7.1, the message was signed, but the signature contained syntax errors or could not otherwise be processed.

What it means

The verifier saw a signature but reached neither pass nor fail. RFC 8601 also uses neutral for failures not covered by the other results, so implementations vary in what they report under it.

Common emission cases

  • The signing algorithm is one the verifier does not implement (e.g., ed25519-sha256 per RFC 8463 at a receiver that only supports RSA), so the signature cannot be processed.
  • The verifier policy treats certain edge cases as neutral rather than fail. Some verifiers emit neutral when a l= (body-length-limit) tag is present but the partial-body verification was ambiguous.
  • Local policy quirks — receivers sometimes mark questionable signatures neutral pending policy refinement.

Operational consequence

Treat dkim=neutral like dkim=none for DMARC alignment purposes — it does not produce a DKIM-pass-and-align. Unlike fail it is not a verifier-rejection; the message is not being branded as unauthentic.

Diagnostic next step

Inspect the optional reason text in the Authentication-Results stanza for hints (some verifiers include the algorithm name or specific policy rule). If volume is concerning, contact the receiver operator — dkim=neutral is rare enough that it usually indicates a niche compatibility issue worth surfacing.

Verdict: dkim=pass

Definition: per RFC 8601 §2.7.1, the signature verified cryptographically — the public key resolved, the algorithm was supported, the body hash matched, and the header signature matched.

What it means

The DKIM verifier successfully completed every protocol step:

  1. Looked up <s=>._domainkey.<d=> TXT and retrieved a valid v=DKIM1; k=<algorithm>; p=<public-key> record.
  2. Computed the body hash per the c= tag's canonicalization rules and confirmed it matches the bh= tag.
  3. Reconstructed the signed-header set per the h= tag and verified the signature using the public key.
  4. Verified the algorithm in the a= tag is supported and not deprecated.

What it does NOT mean

dkim=pass does not mean DMARC passes. Two messages can both have dkim=pass with different DMARC outcomes:

# Aligned - DMARC pass
From: alice@example.com
DKIM-Signature: ... d=example.com s=selector1 ...
Authentication-Results: dkim=pass header.d=example.com header.from=example.com
DMARC: pass (alignment via DKIM)

# Misaligned - DMARC fail
From: alice@example.com
DKIM-Signature: ... d=tenant.onmicrosoft.com s=selector1 ...
Authentication-Results: dkim=pass header.d=tenant.onmicrosoft.com header.from=example.com
DMARC: fail (no aligned authentication)

DMARC requires the d= tag to MATCH the From: header per RFC 7489 §3.1 — exact match for strict alignment, organizational-domain match (via the Public Suffix List) for relaxed alignment. The M365 onmicrosoft.com gotcha and the Workspace gappssmtp.com gotcha both produce dkim=pass with DMARC fail because the signing domain is the tenant routing domain rather than the custom domain.

Diagnostic next step

If dkim=pass but DMARC fails: read DMARC alignment for the alignment rules and inspect the header.d versus header.from in the Authentication-Results.

Verdict: dkim=fail

Definition: per RFC 8601 §2.7.1, signature evaluation completed but the cryptographic verification failed — the public key resolved but did not validate the signature.

What it means

The verifier got far enough to attempt cryptographic verification but the comparison failed. This is structurally different from permerror (could not even attempt verification) and temperror (could not retrieve the key).

Common causes — ordered by frequency

  1. Body modification by a forwarder — a mailing list appended a footer ("You are receiving this because you subscribed to..."), a security gateway rewrote URLs through a sandbox, an outbound relay normalized line-endings. The body hash bh= no longer matches.
  2. MIME re-encoding by an intermediate MTA — base64 ↔ quoted-printable conversion, whitespace normalization in text/plain bodies. Any byte change invalidates the body hash.
  3. Header modification by an anti-spoof rewriter — Microsoft Defender's "From:" header rewrite for unauthenticated mail triggered for the wrong reason; anti-phishing scanners modifying Subject prefixes.
  4. Body-hash canonicalization mismatch — sender signed with c=simple/simple (strict) but a downstream MTA changed even a single byte; c=relaxed/relaxed is the modern default for forwarder tolerance.
  5. Key mismatch — the operator rotated the key but the old TXT was retired before in-flight signed messages cleared the system.

What dkim=fail almost always IS

A FORWARDER or LIST problem. The original sender's signature was valid when it left the sending MTA; something between sender and final receiver modified the message. ARC sealing per RFC 8617 is the protocol-level fix — see ARC explained.

What dkim=fail rarely is

A genuine sender-side configuration failure. If the sender's outbound signing was broken, you would see dkim=fail CONSISTENTLY across all messages — not the intermittent dkim=fail that forwarder rewrites produce.

Diagnostic next step

  • Check whether the failing messages share a common X-Mailing-List / List-Id / forwarder signature header — a single source usually points at a single forwarder problem.
  • If forwarders are the cause, the ARC seal has to be added by the intermediary and trusted by the receiver (Defender for Office 365 has a Trusted ARC Sealers list; Google Workspace seals with ARC automatically).
  • If the failure is consistent across all messages, suspect sender-side configuration: signing module broken, key rotated incorrectly, MTA stripped the signature before transmission. Send a test message and inspect the DKIM-Signature header BEFORE the forwarder hop.

Verdict: dkim=policy

Definition: per RFC 8601 §2.7.1, the signature was evaluated against a LOCAL receiver policy and the policy rejected it.

What it means

The signature itself may have been cryptographically valid, but the receiver applied a stricter local rule than the minimum RFC 6376 spec — and that rule rejected the signature.

Common policy rejections

  • Forbidden algorithm — rsa-sha1 is forbidden by RFC 8301, which requires signing and verifying with rsa-sha256 only; receivers report such a signature as policy or permerror.
  • Weak key size — RFC 8301 permits RSA keys from 1024 bits and recommends at least 2048; keys shorter than 1024 bits are a permanent failure. Strict receivers may reject 1024-bit keys by policy as well, so 2048-bit is the baseline.
  • Signature too old — the t= (signing timestamp) tag is older than the receiver-configured maximum. A signature past its x= (expiration) tag is already a PERMFAIL under RFC 6376, i.e. permerror.
  • Missing required header in h= tag — the receiver requires From, Subject, Date, To, and Message-ID all covered by the signature, but the h= list omits one of them.
  • Suspicious signing-domain pattern — receiver detected the d= tag is a freshly-registered throwaway or matches a known abuse catalogue.

Operational consequence

Like dkim=fail, dkim=policy does not provide DMARC alignment. Unlike dkim=fail the fix is almost always sender-side — you must upgrade your signing posture to meet the receiver's policy.

Diagnostic next step

  • If the reason includes "weak key" or "key size", rotate to 2048-bit RSA per RFC 8301. See DKIM key rotation.
  • If the reason includes "algorithm", ensure your signer uses a=rsa-sha256 (not rsa-sha1).
  • If the reason includes "expired", extend the window of the x= tag or find out why messages arrive that late.
  • If the reason is missing or generic, contact the receiver operator — local policy rules are not RFC-normative and often need direct communication to diagnose.

Verdict: dkim=temperror

Definition: per RFC 8601 §2.7.1, evaluation hit a TEMPORARY error that prevented completion — typically a DNS timeout while looking up the public key.

What it means

The verifier began evaluation but could not complete it. The signature is not necessarily invalid; the verifier just could not retrieve the public-key TXT in time. RFC 6376 calls this result TEMPFAIL and lets the verifier defer the message for later processing or try another signature (§6.1); RFC 8601 adds that a later attempt may produce a final result. Until the key is retrieved, the signature does not count as valid.

Common causes

  • Authoritative nameserver outage — the operator's primary DNS host has a momentary outage while the verifier is querying.
  • Recursive resolver flakiness — the receiver's resolver is overloaded or experiencing transient packet loss.
  • CNAME-chain timeout — for M365 DKIM CNAMEs that resolve through the tenant onmicrosoft.com target, a slow resolution chain can time out before the TXT at the chain's end is retrieved.
  • Anycast routing changes — DNS anycast pools occasionally shift traffic between sites mid-lookup; verifiers with aggressive timeouts hit these shifts as temperrors.

How to read temperror in aggregate reports

Isolated temperror rows (one or two from a single receiver in a 24-hour window) are routine and self-resolve.

Concentrated temperror clusters tell you something:

  • If many rows from ONE receiver are temperror in a tight time window, that receiver's resolvers had an outage — not your problem.
  • If many rows from ALL receivers are temperror across a 24-hour window, YOUR authoritative nameserver is flaky. Investigate your DNS provider.

Diagnostic next step

  • Run continuous external DNS probes against <your-selector>._domainkey.<your-domain> from multiple geographies for 24-48 hours. If the probes show consistent sub-100ms responses, the issue is receiver-side.
  • Validate the CNAME chain depth — long chains amplify lookup latency. M365 CNAMEs typically chain through 2 hops; more than 3 is suspicious.
  • Verify TTL values are reasonable (3600-86400 for steady-state); very low TTLs increase lookup load and timeout risk.

Verdict: dkim=permerror

Definition: per RFC 8601 §2.7.1, evaluation hit a PERMANENT error that prevented completion — the verifier could not even attempt cryptographic verification.

What it means

Something is structurally wrong with the signature or its surrounding context such that the verifier cannot perform the crypto. Unlike temperror this is not retryable — the next lookup will produce the same permerror.

Common causes

  • Public-key TXT does not exist — the <s>._domainkey.<d> query returns NXDOMAIN. Most common after a botched rotation that retired the old selector's TXT before in-flight signed messages cleared the system.
  • Public-key TXT is empty or revoked — the TXT contains v=DKIM1; k=rsa; p= with no public-key bytes (Workspace explicit revocation pattern).
  • Public-key TXT is malformed — invalid base64, wrong tag set, broken multi-string chunking past the 255-byte TXT character-string limit per RFC 1035 §3.3.14.
  • DKIM-Signature header is syntactically broken — missing required tags (v=, a=, d=, s=, h=, b=, bh=), invalid base64, malformed canonicalization spec.
  • Algorithm forbidden or not supported — the a= tag specifies rsa-sha1, which RFC 8301 treats as permanently failed, or an algorithm the verifier does not know at all (the latter is also reported as neutral).

Operational consequence

Like dkim=fail and dkim=policy, permerror does not produce DMARC alignment. Unlike fail, permerror almost always points at a sender-side publishing or signing problem, not a forwarder.

Diagnostic next step

  • Look up the public-key TXT yourself: dig +short TXT <s=>._domainkey.<d=>. If NXDOMAIN, the publishing side is broken.
  • Validate the TXT format with a DKIM record-syntax tool (or Domain Security Checker's DKIM block).
  • Pull the failing message's full DKIM-Signature header (header.b= is in the Authentication-Results) and run it through Email Header Analyzer for the structural validation.

DKIM verdicts versus DMARC alignment

The single most-confused topic in DKIM diagnostics. DKIM verification is separate from DMARC alignment.

DKIM verification answers: "Did the signature cryptographically verify?" The answer is the verdict — one of the seven keywords above.

DMARC alignment answers: "Does the d= tag of a dkim=pass signature match (strict) or share an organizational domain (relaxed) with the From: header?"

Both must succeed for DKIM-based DMARC authentication. The standard error cases:

Case A: dkim=pass + DMARC pass

From: alice@example.com
DKIM-Signature: ... d=example.com s=selector1 ...
Authentication-Results: dkim=pass header.d=example.com header.from=example.com
DMARC: pass (DKIM aligned)

The happy path. d= equals From: so DMARC is satisfied.

Case B: dkim=pass + DMARC fail (M365 onmicrosoft.com gotcha)

From: alice@example.com
DKIM-Signature: ... d=tenant.onmicrosoft.com s=selector1 ...
Authentication-Results: dkim=pass header.d=tenant.onmicrosoft.com header.from=example.com
DMARC: fail (no aligned authentication)

Signature is valid (dkim=pass) but d=tenant.onmicrosoft.com does not match From=example.com — alignment fails. See DKIM for Microsoft 365 for the M365 fix.

Case C: dkim=pass + DMARC fail (Workspace gappssmtp.com gotcha)

From: alice@example.com
DKIM-Signature: ... d=tenant.gappssmtp.com s=20221208 ...
Authentication-Results: dkim=pass header.d=tenant.gappssmtp.com header.from=example.com
DMARC: fail (no aligned authentication)

Same structural problem as Case B but on Google Workspace. See DKIM for Google Workspace for the Workspace fix.

Case D: dkim=fail or dkim=permerror — alignment irrelevant

Without a dkim=pass verdict there is no DKIM-based alignment to evaluate. DMARC falls back to SPF-based alignment (or fails entirely if SPF also fails or misaligns).

Reading DKIM verdicts in DMARC aggregate reports

DMARC aggregate (rua) reports per RFC 7489 §7.2 include a <auth_results><dkim> block per signature evaluated. Schema:

<auth_results>
    <dkim>
        <domain>example.com</domain>
        <selector>selector1</selector>
        <result>pass</result>
        <human_result></human_result>
    </dkim>
    <dkim>
        <domain>list.example.org</domain>
        <selector>2024a</selector>
        <result>fail</result>
        <human_result>body hash did not verify</human_result>
    </dkim>
</auth_results>

The <result> element uses the same seven keywords this guide documents (pass, fail, neutral, temperror, permerror, none, policy). The <human_result> element optionally carries the receiver's free-text failure reason — exceptionally valuable for diagnosis when present.

DMARC Analyzer's report drill-down

The Reports page (parsed via includes/DmarcParser.php) groups DKIM verdicts by:

  • Reporter — who issued the verdict (mail.google.com, outlook.com, mx.yahoo.com, etc.). A single reporter producing all the failures usually = a forwarder problem at that one receiver.
  • Source IP — what sending IP the message came from. A single source IP producing all the failures usually = a misconfigured outbound MTA.
  • Signing domain (header.d) — what d= tag the signer used. Useful for catching the M365 onmicrosoft.com and Workspace gappssmtp.com gotchas.
  • Time window — when the failures occurred. Concentrated time clusters often correlate with deployment events (key rotation, signer migration, MTA upgrade).

The dashboard's per-reporter breakdown is the fastest way to localize "is this failure my fault or the receiver's?" — typically the answer is visible in seconds.

Per-verdict decision matrix

Map verdict to action:

  • dkim=none → DKIM is not enabled at the sending platform. Verify Get-DkimSigningConfig (M365) or Admin Console status (Workspace); enable signing for the custom domain.
  • dkim=neutral → Rare; inspect the reason text; usually a niche algorithm or policy edge case. Contact receiver if volume is concerning.
  • dkim=pass + DMARC fail → Alignment problem, not a DKIM problem. Check header.d vs header.from; almost always the M365 onmicrosoft.com or Workspace gappssmtp.com gotcha.
  • dkim=fail → Almost always a forwarder/list rewriting the body. Configure ARC sealing at the outbound MTA. If consistent across all messages, suspect sender-side signer breakage.
  • dkim=policy → Sender-side posture upgrade needed. Rotate to 2048-bit RSA + rsa-sha256; remove the x= tag if present; ensure h= covers From, Subject, Date, To, Message-ID.
  • dkim=temperror → DNS lookup transient failure. If isolated, ignore. If concentrated from one receiver, the receiver's DNS had an outage. If concentrated across all receivers, your authoritative nameserver is flaky.
  • dkim=permerror → Sender-side publishing or signing problem. dig the public key, validate the TXT format, parse the DKIM-Signature header structurally.

Frequently asked questions

What does dkim=none mean in Authentication-Results?
dkim=none per RFC 8601 §2.7.1 means the message arrived with no DKIM-Signature header at all - the verifier had nothing to evaluate. The most common cause is that the sending domain never enabled DKIM signing for outbound mail. On Microsoft 365 the fix is Set-DkimSigningConfig -Identity <your-domain> -Enabled $true after publishing the two CNAMEs; on Google Workspace it is the Start authentication click in the Admin Console after publishing the google._domainkey TXT. dkim=none is NOT a failure per se - it indicates absence rather than verification failure - but it is fatal for DMARC because DMARC requires at least one of DKIM or SPF to align with the From: header domain. When you see dkim=none in DMARC aggregate reports for messages your domain sent, the diagnostic next step is to verify DKIM is enabled at the sending platform.
What is the difference between dkim=fail and dkim=permerror?
dkim=fail means the signature was evaluated and FAILED cryptographically - the public key resolved correctly but the signature did not verify against the message content. Common causes: body modification by a forwarder, mailing-list footer injection, MTA header rewriting, body-hash mismatch from MIME re-encoding. dkim=permerror means the signature could not be evaluated at all due to a permanent error - the public key TXT does not exist or is malformed, the d= domain has no DNS record at the selector, the signature header itself is syntactically broken, or the algorithm is unsupported. The diagnostic distinction matters: dkim=fail = signature reached the verifier but the content was tampered (operationally a forwarder/list problem); dkim=permerror = the verifier could not even attempt verification (operationally a publishing/configuration problem). RFC 6376 §3.9 defines three verifier outputs (SUCCESS, PERMFAIL, TEMPFAIL); RFC 8601 §2.7.1 defines the seven dkim=X keywords that appear in Authentication-Results.
What does dkim=temperror mean and should I be worried?
dkim=temperror means the verifier hit a temporary failure that prevented evaluation - usually a DNS timeout while looking up the public key at <s=>._domainkey.<d=>. The signature itself may be valid; the verifier just could not retrieve the key in time. Common causes: authoritative nameserver outage, recursive resolver flakiness, transient network failure between verifier and the publishing nameserver, CNAME-chain timeout (M365 CNAMEs that resolve through the tenant target onmicrosoft.com). Per RFC 8601 a later attempt may produce a final result, and RFC 6376 §6.1 lets the verifier defer the message for later processing or try another signature - until the key is retrieved the signature does not count as valid. If you see ISOLATED dkim=temperror rows in aggregate reports they are usually transient and self-resolve. If you see CONCENTRATED dkim=temperror clusters (many rows from the same receiver, same time window) then your authoritative nameservers may be under-resourced or there is a CNAME-chain problem.
What does dkim=neutral mean?
dkim=neutral per RFC 8601 §2.7.1 means the message was signed, but the signature contained syntax errors or could not otherwise be processed. The RFC also uses it for failures not covered by the other results. The most common modern case is a signature using an algorithm the verifier does not implement (e.g., ed25519-sha256 per RFC 8463 at a receiver that only supports RSA), or an edge case a verifier prefers to report as neutral rather than fail. dkim=neutral is rare in practice and implementations vary in what they report under it. RFC 8601 adds that neutral, like none, means the message will be treated as if it had not been signed, so treat dkim=neutral like dkim=none for DMARC alignment purposes (it does not produce alignment). If you see dkim=neutral in aggregate reports at a concerning volume, check the syntax of your own signature and contact the receiving operator.
What is dkim=policy and when does it appear?
dkim=policy per RFC 8601 §2.7.1 means the message was signed, but some aspect of the signature was not acceptable to the receiving domain - a LOCAL receiver policy rejected it. Common policy rejections: a key the receiver considers too weak (RFC 8301 permits RSA from 1024 bits and recommends at least 2048, and strict receivers may demand more than the minimum), a signature older than the receiver accepts (the t= timestamp tag), a third-party signing domain the receiver does not accept, or an h= tag that does not cover a header the receiver requires signed per its local policy. Some receivers also report rsa-sha1 signatures as policy, although RFC 8301 treats them as permanently failed. dkim=policy is a receiver-side rejection, distinct from the protocol-level dkim=fail. The operational fix is sender-side: sign with rsa-sha256, upgrade to 2048-bit keys, and ensure the h= tag covers From (an RFC 6376 requirement) plus Subject, Date, To and Message-ID. See RFC 8301 for the modern algorithm/key-size requirements.
My DKIM signature says dkim=pass but DMARC still fails - why?
Because dkim=pass evaluates ONLY the cryptographic signature, not DMARC ALIGNMENT. DMARC requires the DKIM-Signature d= tag to ALIGN with the From: header domain (RFC 7489 §3.1) - either strict (exact match) or relaxed (organizational-domain match via PSL). The most common alignment-failure pattern is the M365 onmicrosoft.com gotcha (dkim=pass header.d=tenant.onmicrosoft.com but header.from=example.com - signature passes, alignment fails) and the Workspace gappssmtp.com gotcha (dkim=pass header.d=tenant.gappssmtp.com but header.from=example.com). The fix is to enable DKIM signing for the CUSTOM domain at the sending platform so the d= tag points at example.com instead of the tenant routing domain. See help_dmarc_alignment.php for the alignment-evaluation rules and help_dkim_for_microsoft_365.php / help_dkim_for_google_workspace.php for the per-provider fixes.
How do I find DKIM failure verdicts in DMARC aggregate reports?
DMARC aggregate (rua) reports per RFC 7489 §7.2 include, in every report row, an <auth_results> block with one <dkim> element for each evaluated signature. Each block contains a <domain> (the DKIM d= tag), a <selector> (the s= tag), and a <result> element that is one of pass, fail, neutral, temperror, permerror, none, or policy (matching the RFC 6376 §3.9 status keywords exposed via RFC 8601 §2.7.1). DMARC Analyzer parses these blocks via includes/DmarcParser.php and surfaces the per-domain DKIM verdict breakdown on the dashboard. To pivot from "I see dkim=fail in my Authentication-Results header" to "show me all rua rows for that domain and selector", upload the rua XML or feed it via IMAP/Graph and open the Reports drill-down. The drill-down groups DKIM verdicts by reporter so you can isolate whether a single receiver is producing the failures (likely a forwarder/list problem on their side) or all receivers are (likely a sender-side configuration problem).
Can DKIM verdicts differ between receivers for the same message?
Yes, by design. RFC 6376 leaves DKIM verification policy choices to receivers, and RFC 8601 only specifies the verdict vocabulary - not the verdict-emission rules. Three common divergence patterns: (1) Forwarder rewrites the message body between hop 1 (receiver A reports dkim=pass for the original signature) and hop 2 (receiver B reports dkim=fail because body-hash mismatches after the rewrite) - solved by ARC sealing per RFC 8617. (2) Local-policy strictness: receiver A accepts a signature made with a 1024-bit key as dkim=pass while receiver B emits dkim=policy on the same message - solved by sender-side migration to 2048-bit keys and rsa-sha256. (3) DNS-cache state: receiver A had the DKIM TXT cached from a prior lookup while receiver B emits dkim=temperror because its lookup hit a transient nameserver outage. The DMARC aggregate-report ecosystem reveals this divergence by reporting receiver-by-receiver verdicts; the dashboard pivot view shows where consistency breaks down.

Localize a DKIM failure in your reports now

DMARC Analyzer parses the <auth_results><dkim> blocks of every aggregate report via includes/DmarcParser.php and surfaces the per-domain, per-selector, per-reporter verdict breakdown on the dashboard. The Reports drill-down lets you pivot from "I see dkim=fail" to "here are the 47 rua rows that produced that failure, grouped by reporter and source IP". The Email Header Analyzer at header_analyzer.php structurally validates individual DKIM-Signature headers via includes/EmailHeaderParser.php — paste a failing message's headers and see the verdict breakdown plus the DKIM-Signature tag-by-tag parse.

Sign up — Free plan Analyze a message header