DKIM Alignment with DMARC: The d= Tag as the DMARC Alignment Anchor (RFC 7489 §3.1.1 — adkim=r vs adkim=s with worked examples, the Public Suffix List organizational-domain extraction algorithm, the multiple-signature "any aligns wins" rule, the M365 onmicrosoft.com gotcha, the Workspace gappssmtp.com gotcha, the SPF-vs-DKIM asymmetry under forwarding, and subdomain semantics edge cases)

The DKIM-side complement to help_dmarc_alignment.php. Where the DMARC alignment page documents alignment from the DMARC-engine angle (covers both SPF and DKIM symmetrically), this page is the focused DKIM-only deep dive into how the d= tag in the DKIM-Signature header feeds into the DMARC alignment decision per RFC 7489 §3.1.1. Closes the gap left by the verdict catalogue (help_dkim_failures_explained.php) and the sender-side tag anatomy (help_dkim_signatures_explained.php). Covers: the d= tag as the DMARC alignment anchor (RFC 7489 §3.1.1 names d=, not i=); adkim=r (relaxed) vs adkim=s (strict) with worked examples; the PSL organizational-domain extraction algorithm including the canonical .co.uk illustration and multi-tenant PSL entries (s3.amazonaws.com, workers.dev, gappssmtp.com); the multiple-signature "any aligns wins" rule per §3.1.1; the M365 onmicrosoft.com gotcha in full alignment-evaluation detail; the Workspace gappssmtp.com gotcha (the PSL-listed multi-tenant signing domain); the SPF-vs-DKIM asymmetry under forwarding (DKIM survives because d= is inside the signed-header set; SPF breaks because forwarders rewrite the envelope); subdomain semantics edge cases; 10 common alignment mistakes; and a troubleshooting matrix mapping symptom → likely cause → next step.

This reference is provided in English. RFC 7489 §3.1.1 alignment semantics and the canonical DKIM/DMARC vocabulary (d=, i=, adkim=r, adkim=s, "organizational domain", "Public Suffix List", onmicrosoft.com, gappssmtp.com) do not translate. We keep the body in the source language so technical terms remain consistent with IETF RFC documentation.

TL;DR

  • DKIM alignment is the rule that the d= tag in the DKIM-Signature header must match the visible From: header domain. Defined in RFC 7489 §3.1.1.
  • d= drives alignment, not i=. RFC 7489 §3.1.1 names d= as the DKIM-side alignment identifier. The i= AUID tag is ignored for alignment purposes (some receivers use it for non-normative additional checks).
  • adkim=r is the default — organizational-domain match per the Public Suffix List. mail.example.com aligns with example.com. adkim=s requires an exact match; same domains do NOT align under strict.
  • dkim=pass ≠ DMARC DKIM-aligned. A signature must verify cryptographically AND its d= must align with From:. The onmicrosoft.com and gappssmtp.com gotchas are exactly this distinction: dkim=pass for the tenant default but no alignment for the custom From: domain.
  • Multiple signatures, any aligns wins. A message can carry many DKIM-Signature headers; DMARC passes DKIM if AT LEAST ONE valid signature has an aligned d=. Forwarder-added signatures with their own d= are harmless — they cannot help (do not align), and do not hurt (the original aligned signature is still present).
  • DKIM survives forwarding when SPF does not. Forwarders rewrite the SMTP envelope and break SPF alignment, but the d= tag is inside the signed-header set and travels with the message. As long as the forwarder does not modify the body or strip the DKIM-Signature header, DKIM alignment survives.
  • PSL is authoritative for relaxed. RFC 7489 §3.2 names the Public Suffix List as the source of public suffixes for organizational-domain extraction. For example.co.uk the organizational domain is example.co.uk, not co.uk — the PSL prevents the wrong reduction.
  • Fix path for misaligned SaaS senders: enable custom-domain DKIM at the SaaS provider so it signs with d=<your-domain> instead of its own bounce/tenant domain. For M365 that means the Defender portal CNAME publication; for Workspace that means the Admin Console Generate-new- record TXT publication; for SendGrid / Mailgun / Mailchimp / Postmark / etc. the per-provider equivalent.

What this guide covers

The DKIM-side complement to DMARC alignment explained. Where the DMARC alignment page documents alignment from the DMARC-engine angle (covers both SPF and DKIM symmetrically), THIS page is the focused DKIM-only deep dive into how the d= tag in the DKIM-Signature header feeds into the DMARC alignment decision per RFC 7489 §3.1.1.

The page closes the gap left by the verdict catalogue (DKIM failures explained) and the sender-side tag anatomy (DKIM-Signature header explained) — both pages reference DMARC alignment but neither does the full alignment-engine deep dive from the DKIM angle. Together the three pages form a triangle: tags (what the signer emits), verdicts (what the verifier outputs), alignment (how the verdict feeds into DMARC).

Operators arriving here typically have a DMARC failure where DKIM cryptographically passed (dkim=pass) but DMARC failed anyway. The diagnostic is almost always: the d= signing-domain identifier does not align with the From: header domain. The two canonical failure modes — the M365 onmicrosoft.com gotcha and the Workspace gappssmtp.com gotcha — get their own deep-dive sections below.

The d= tag is the alignment anchor

RFC 7489 §3.1.1 specifies that the DMARC DKIM-side alignment identifier is the d= tag of the DKIM-Signature header — NOT the i= AUID tag. The exact wording:

RFC 7489 §3.1.1 (DKIM-authenticated Identifiers):

  In relaxed mode, the Organizational Domains of both the
  [DKIM]-authenticated signing domain (taken from the value
  of the "d=" tag in the signature) and that of the RFC5322.From
  domain must be equal if the identifiers are to be considered
  aligned.

  In strict mode, only an exact match between both of the
  Fully Qualified Domain Names (FQDNs) is considered to produce
  Identifier Alignment.

The d= tag identifies the domain on whose behalf the message was signed. It is the domain whose public-key TXT record under <s>._domainkey.<d> the verifier used to validate the signature.

The i= AUID (Agent or User Identifier) tag, when present, identifies a specific user or sub-agent on whose behalf the message was signed — typically a local-part@d= construction like i=alice@example.com or just @example.com. RFC 7489 §3.1.1 explicitly excludes i= from alignment evaluation: only the d= value participates in the DMARC match. So a signature with d=example.com; i=alice@example.com aligns identically to a signature with d=example.com and no i= at all.

Some receivers (notably some anti-spoof gateways) compare the i= local-part against the From: header local-part for additional verification — but this is a non-normative local policy decision and not part of DMARC. Per the tag anatomy page help_dkim_signatures_explained.php §i= AUID, modern operators typically omit i= entirely because it adds nothing to DMARC and introduces a surface for local-policy surprises.

Why the d= choice is structural, not arbitrary

The DKIM signature itself covers a set of headers selected by the h= tag plus a hash of the message body (bh=). The verifier uses the d= + s= pair to locate the public-key TXT record and verify the b= signature. The d= is therefore the IDENTITY that the signature ATTESTS TO — the signer is making a cryptographic claim "this message is from me, where 'me' is d=<value>". DMARC's job is to confirm that the identity the signer attests to matches the identity the recipient sees in From:. The i= tag, by contrast, is a sub-identifier within d= — it does not change WHO is signing, only WHICH sub-agent of that signer is responsible. For DMARC's "who signed this" question, only d= answers.

Alignment modes: adkim=r vs adkim=s

The DMARC policy record carries the adkim= tag which controls how strictly the receiver compares d= against From:. Two values are defined per RFC 7489 §6.3:

  • adkim=r — relaxed, the default. The organizational domains of d= and the From: header must match, determined via the Public Suffix List (next section).
  • adkim=s — strict. The d= value must equal the From: header domain byte-for-byte (case-insensitive ASCII).

If the DMARC record omits adkim=, the default per §6.3 is r. Most production DMARC records do not bother setting adkim explicitly and rely on the relaxed default.

Worked alignment examples

Given a From: header of alice@example.com:

d=example.com            adkim=r ALIGNED   adkim=s ALIGNED
d=mail.example.com       adkim=r ALIGNED   adkim=s NOT ALIGNED
d=news.example.com       adkim=r ALIGNED   adkim=s NOT ALIGNED
d=example.co             adkim=r NOT       adkim=s NOT (different org domain)
d=example.org            adkim=r NOT       adkim=s NOT (different TLD)
d=onmicrosoft.com        adkim=r NOT       adkim=s NOT (Microsoft tenant)
d=tenant.gappssmtp.com   adkim=r NOT       adkim=s NOT (Workspace tenant)
d=EXAMPLE.com            adkim=r ALIGNED   adkim=s ALIGNED (case-insensitive)

The interesting cases are the middle two: d=mail.example.com and d=news.example.com align under relaxed because their organizational domain is example.com (the registered name + the public suffix .com), matching the From: organizational domain. Under strict the subdomain mismatch breaks alignment.

When to choose strict

The conventional guidance is:

  • Use adkim=r (relaxed) for active sending organizational domains where transactional mail may be signed with subdomain d= values (e.g. d=mail.example.com for transactional, d=news.example.com for marketing).
  • Use adkim=s (strict) for non-sending domains where any subdomain-level signature should be treated as suspicious, and for high-value brand domains where the operational discipline of "every legitimate sender signs with d=<exact-domain>" is achievable.

Strict alignment closes the subdomain-spoofing surface — an attacker who compromises a subdomain key cannot sign mail for the organizational domain under strict — at the cost of operational flexibility. The DMARC policy rollout page covers the relaxed-to-strict migration sequence.

PSL organizational-domain matching (relaxed mode)

Relaxed alignment (adkim=r) reduces each domain to its organizational domain before comparing. RFC 7489 §3.2 defines the organizational domain as the registered name plus the public suffix immediately to the right of it. The canonical source of public suffixes is the Public Suffix List (PSL) maintained at publicsuffix.org, originally by Mozilla and now community-maintained.

The reduction algorithm per RFC 7489 §3.2:

  1. Find the longest sequence of labels at the end of the FQDN that matches an entry in the PSL. This is the public suffix.
  2. Prepend exactly ONE more label — the registered name.
  3. The concatenation <registered>.<public-suffix> is the organizational domain.

Worked examples:

FQDN                          Public Suffix    Org Domain
example.com                   com              example.com
mail.example.com              com              example.com
deep.sub.example.com          com              example.com
example.co.uk                 co.uk            example.com (NO - actually example.co.uk)
example.co.uk                 co.uk            example.co.uk
mail.example.co.uk            co.uk            example.co.uk
example.s3.amazonaws.com      s3.amazonaws.com example.s3.amazonaws.com
foo.bar.example.s3.amazonaws  s3.amazonaws.com example.s3.amazonaws.com

The example.co.uk case is the canonical illustration of why the PSL is necessary. Without the PSL, a naive "registered name = last two labels" algorithm would reduce example.co.uk to co.uk — which would then make example.co.uk and other.co.uk "aligned" because they share organizational domain co.uk. Clearly wrong. The PSL encodes that co.uk itself is a public suffix (not a registered name) so the algorithm correctly takes one more label and produces example.co.uk as the organizational domain.

The s3.amazonaws.com case illustrates the OTHER direction: some domains are LISTED in the PSL even though they do not look like traditional public suffixes. AWS adds private subdomains to the PSL so that S3 bucket subdomains do not align with each other. Without that listing, every S3 bucket on the same parent would share amazonaws.com as org domain — privacy-invasive in DMARC aggregate reports. Cloudflare Workers, GitHub Pages, Heroku app domains, and many other multi-tenant platforms have similar PSL entries.

Practical implication for operators

Three operational consequences flow from the PSL design:

  • If your domain registers under a country-code TLD with multiple commercial suffixes (.co.uk, .com.au, .co.jp, .com.br), the organizational domain is the full third-level FQDN, not the second-level. DMARC records, policy decisions, and rua reports all operate at that level.
  • If your sending infrastructure lives under a PSL-listed multi-tenant platform (e.g., a SaaS-provided subdomain on *.s3.amazonaws.com, *.workers.dev, *.herokuapp.com), each tenant has its own organizational domain and cannot align with any other tenant under relaxed. You CANNOT use these platforms as your DKIM signing domain for mail with a custom From: — you must use custom-domain DKIM with d=<your-registered-domain>.
  • The PSL is updated continuously by the community. Receivers MAY cache the PSL but are expected to refresh it regularly; in practice all major mailbox providers update at least weekly. An operator publishing a new DMARC record does not need to do anything special with the PSL — they just need to know that relaxed alignment will Do The Right Thing for normal registered-domain mail flows.

The DKIM alignment evaluation algorithm

The receiver evaluates DKIM alignment as follows per RFC 7489 §3.1.1 + §6.6.2:

  1. Extract the RFC5322.From domain from the message — this is the SLD or FQDN in the From: header field that the user-facing client will display.
  2. For each DKIM-Signature header in the message, run the DKIM verifier per RFC 6376. Record each signature's result (pass, fail, none, neutral, policy, temperror, permerror) and the d= value.
  3. For each signature with a pass result, evaluate the DKIM identifier alignment against the From: domain using the policy mode (adkim=r or adkim=s) from the published DMARC record for the From-domain organizational domain.
  4. If ANY pass signature has an aligned d=, DKIM alignment for DMARC = ALIGNED.
  5. If no signature passes, or all passing signatures have misaligned d=, DKIM alignment for DMARC = NOT ALIGNED.

The DMARC engine then evaluates the SPF alignment separately (against RFC5321.MailFrom / Return-Path) and the overall DMARC verdict is pass if either DKIM-aligned or SPF-aligned, else fail.

Worked example: aligned + misaligned signatures coexisting

A message from alice@example.com through a SaaS sender and then a mailing list might carry three DKIM-Signature headers:

DKIM-Signature: ... d=example.com         s=mail        ; b=...  (original)
DKIM-Signature: ... d=saas.example         s=k1          ; b=...  (SaaS wrapping)
DKIM-Signature: ... d=list.example.org     s=20240101    ; b=...  (mailing list rewrap)

RFC5322.From:    alice@example.com
DMARC record:    adkim=r (default)

Evaluation per signature:
  d=example.com         -- dkim=pass, org=example.com    -> ALIGNED
  d=saas.example        -- dkim=pass, org=saas.example   -> NOT ALIGNED
  d=list.example.org    -- dkim=pass, org=example.org    -> NOT ALIGNED

DMARC DKIM alignment = ALIGNED (any aligned wins)
DMARC DKIM-aligned signature = d=example.com
DMARC verdict = pass (DKIM-aligned)

The presence of the SaaS and list signatures does not affect the DMARC outcome — they are non-aligning extras. What matters is that AT LEAST ONE dkim=pass signature has an aligned d=. The receiver's Authentication-Results header typically logs the aligned signature explicitly via the policy.policy-from=example.com or dmarc=pass header.from=example.com construction.

Worked example: all signatures misaligned

DKIM-Signature: ... d=onmicrosoft.com ... b=...  (M365 tenant default)
RFC5322.From:    alice@yourcompany.com
DMARC record:    yourcompany.com, adkim=r

Evaluation:
  d=onmicrosoft.com  -- dkim=pass, org=onmicrosoft.com   -> NOT ALIGNED

DMARC DKIM alignment = NOT ALIGNED
SPF alignment        = (separate evaluation)
DMARC verdict        = pass if SPF-aligned, else FAIL

This is the canonical onmicrosoft.com gotcha pattern — dkim=pass verdict but DKIM alignment fails, so the entire DMARC verdict depends on whether SPF aligns. Section 7 below covers the gotcha in full detail.

Multiple DKIM-Signature headers: which one drives alignment

A message MAY carry multiple DKIM-Signature headers — RFC 6376 §3.5 explicitly allows this. Common cases:

  • Original sender + SaaS wrapping: the originating application signs with d=<your-domain>, then the SaaS gateway (SendGrid, Mailgun, Postmark) ALSO signs with their own platform domain (d=sendgrid.net, d=mailgun.org) before relay. Both signatures travel together.
  • Original sender + mailing list: the original sender signs with d=<your-domain>; the mailing list (Mailman, Discourse, Google Groups) modifies the body (adding a footer, rewriting Subject: with a list-prefix) and adds its own signature with d=<list-domain>. The original signature may break (body modification → bh= mismatch) while the list signature passes — alignment outcome depends on whether either passes AND aligns.
  • Per-header rotation / algorithm bridging: a signer emits two signatures simultaneously with different selectors or algorithms — e.g., one with s=20231101 for receivers on the new key and one with s=20230501 for slow DNS-cache receivers during a rotation window.
  • ARC sealing: the ARC chain adds ARC-Message-Signature headers, which are NOT DKIM-Signature headers per se but mirror the DKIM signing structure. ARC sealers and DKIM signers can coexist on the same message.

The "any aligns wins" rule in detail

RFC 7489 §3.1.1 (and §6.6.2 for the enforcement step) is unambiguous: any verified DKIM signature whose d= identifier aligns with the From: domain satisfies DKIM alignment for DMARC. The receiver evaluates each signature independently, and the FIRST aligned pass-result determines the DMARC outcome. Other signatures (passing-but-misaligned, failing, or with malformed tags) do not affect the result.

Operational consequence: an operator does NOT need to remove or worry about forwarder-added or SaaS-added DKIM signatures with non-aligned d= values. They are inert from DMARC's perspective. The only thing that matters is whether at least one signature with an aligned d= survives the delivery path intact.

What goes in the rua aggregate report

DMARC aggregate (rua) XML reports per RFC 7489 §7.2 include the per-signature DKIM results in an <auth_results><dkim> block. When multiple signatures are present, the report typically lists each one — operators reading the report should look for the signature with <domain> matching the From: organizational domain. See help_dkim_failures_explained.php §Aggregate reports for the XML format.

The Microsoft 365 onmicrosoft.com gotcha

When you provision a Microsoft 365 tenant, Microsoft assigns it a default routing domain in the form <tenant-name>.onmicrosoft.com — for example, contoso.onmicrosoft.com for the Contoso Corporation tenant. Microsoft auto-provisions DKIM signing for this tenant default with d=<tenant-name>.onmicrosoft.com — the signature is cryptographically valid (dkim=pass at receivers) but it signs as the tenant default, not as your custom domain.

The failure mode follows directly:

From:           alice@contoso.com
DKIM-Signature: ... d=contoso.onmicrosoft.com ... b=...

At the receiver:
  DKIM crypto verification = PASS (Microsoft's key, valid signature)
  DKIM alignment to From   = NOT ALIGNED (d=onmicrosoft.com != From: contoso.com)
  DMARC DKIM alignment     = FAIL
  DMARC verdict            = depends on SPF alignment
                              if SPF-aligned -> DMARC PASS
                              if SPF misaligned -> DMARC FAIL

The gotcha bites in two scenarios:

  1. You have not enabled custom- domain DKIM in Defender at all. Microsoft signs all outbound with the tenant default, so every message has the misaligned d=.
  2. You enabled custom-domain DKIM but only for the parent domain, not for an alias domain you actually send from. Mail from the alias falls back to the tenant default signature.

The fix

Enable custom-domain DKIM for every sending domain via the Microsoft Defender portal. The procedure is documented in full in help_dkim_for_microsoft_365.php; the high-level steps:

  1. Sign in to Microsoft Defender portal at security.microsoft.com.
  2. Navigate Policies & rules > Threat policies > DKIM under Rules.
  3. Select the custom domain in the table. Microsoft displays the two CNAME values for selector1 and selector2.
  4. Publish the two CNAME records at your DNS provider — selector1._domainkey.<your-domain> and selector2._domainkey.<your-domain> pointing at the values Microsoft showed.
  5. Return to the Defender DKIM page and toggle Enabled = On. Microsoft begins signing outbound with d=<your-domain> on the next message.

Verifying the fix

Send a test message to a Gmail account (Gmail surfaces full DKIM details in Show Original) and inspect the Authentication-Results header. You should see:

Authentication-Results: mx.google.com;
       dkim=pass header.i=@yourcompany.com header.s=selector1
            header.b=AbCdEf12;
       spf=pass (google.com: domain of bounces@yourcompany.com designates ...);
       dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=yourcompany.com

The critical field is header.i=@yourcompany.com (or equivalently the header.d=yourcompany.com in some receivers' formats). If you still see header.i=@contoso.onmicrosoft.com, DKIM is still signing with the tenant default — re-verify the CNAME publication and Defender toggle.

The Google Workspace gappssmtp.com gotcha

Workspace has its own equivalent of the M365 tenant-default failure mode. When you provision a Workspace tenant and have not yet enabled custom-domain DKIM via the Admin Console, Google signs outbound mail with a default identity using d=<your-domain>.<tenant-id>.gappssmtp.com or a similar Workspace-default form. The signature is cryptographically valid (dkim=pass at the receiver) but the signing domain (<...>.gappssmtp.com) does not align with your From: domain.

From:           alice@contoso.com
DKIM-Signature: ... d=contoso.com.123456abcd.gappssmtp.com ... b=...

At the receiver:
  DKIM crypto verification = PASS (Google's key, valid signature)
  DKIM alignment to From   = NOT ALIGNED (gappssmtp.com != contoso.com)
  DMARC DKIM alignment     = FAIL
  DMARC verdict            = depends on SPF alignment

Unlike M365's <tenant>.onmicrosoft.com pattern, the Workspace default signing domain often INCLUDES your real domain as a label in the FQDN — but a label match is not enough for DMARC alignment. contoso.com.123456abcd.gappssmtp.com has organizational domain gappssmtp.com (per PSL — yes, gappssmtp.com is on the Public Suffix List as a Google-controlled multi- tenant domain), which does not align with contoso.com.

The fix

Enable custom-domain DKIM in the Google Workspace Admin Console for every sending domain. Procedure documented in help_dkim_for_google_workspace.php; the high-level steps:

  1. Sign in to the Admin Console at admin.google.com as a super-admin.
  2. Navigate Apps > Google Workspace > Gmail > Authenticate email.
  3. Select your sending domain. Click Generate new record. Choose 2048-bit key strength (per RFC 8301) and accept the default selector google.
  4. Google displays the TXT record value for google._domainkey.<your-domain>. Publish it at your DNS provider. Use 255-byte chunked strings per RFC 1035 §3.3.14 if your DNS provider does not chunk automatically.
  5. After publication, return to the Authenticate email page and click Start authentication. Google begins signing outbound with d=<your-domain> s=google on the next message.

Verifying the fix

Send a test message to an account on a different provider (Outlook, FastMail) and inspect the Authentication-Results header:

Authentication-Results: mx-out.outlook.com;
       dkim=pass header.d=yourcompany.com header.s=google
            header.b=PqRsTu12;
       spf=pass smtp.mailfrom=yourcompany.com;
       dmarc=pass action=none header.from=yourcompany.com

The critical field is header.d=yourcompany.com s=google. If you still see header.d=<...>.gappssmtp.com, Google is still signing with the default — re-verify the TXT publication and the Start-authentication toggle.

Why DKIM alignment survives forwarding when SPF does not

SPF and DKIM align on different identifiers, and the consequence is a structural asymmetry under message forwarding:

  • SPF aligns the RFC5321.MailFrom (SMTP envelope sender / Return-Path). When a forwarder receives a message and re-injects it, the forwarder REWRITES the envelope to its own bounce address — the original envelope domain is replaced with the forwarder's domain. The SPF check on the forwarded delivery authenticates the forwarder, not the original sender. SPF alignment breaks.
  • DKIM aligns the d= tag, which is INSIDE the signed-header set. The DKIM signature covers the headers selected by h= plus a body hash (bh=). As long as the forwarder does not modify the body (which would break bh=) and does not strip the DKIM-Signature header, the original signature travels intact through the forward. The d= value remains whatever the original signer set it to, and DKIM alignment still evaluates against the original signing domain.

What can break DKIM through forwarding

Three things break DKIM signatures during forwarding:

  1. Body modification — mailing lists adding a footer ("To unsubscribe..."), security gateways injecting a "[EXTERNAL]" prefix into the body, automated translation services rewriting body text. Any of these changes the body bytes so the receiver computes a different hash than the published bh=. dkim=fail at the receiver. The fix is forwarder-side: use ARC sealing (see help_arc_explained.php) so the receiver can validate the forwarder's attestation that the original signature was valid at ingress.
  2. Header modification — security gateways prefixing the Subject: with "[EXTERNAL]" or "[CAUTION]". If Subject: was in the original h= list (which it almost always is for modern signers), the modified header breaks the b= signature check. dkim=fail. Fix: ARC sealing.
  3. Re-encoding of the body Content-Transfer-Encoding — relays that convert quoted-printable to base64 or normalize line endings can change body bytes even when the "content" is semantically identical. With c=relaxed/relaxed canonicalization most line-ending normalization is tolerated; with c=simple/simple it breaks. Modern signers use relaxed/relaxed to maximize forward-survivability.

Operational consequence

Because DKIM is the resilient alignment path through real-world mail flow, EVERY sending source should be DKIM-signed with custom-domain DKIM enabled — including every SaaS sender that emits mail with your From: domain (SendGrid, Mailgun, Mailchimp, Postmark, HubSpot, Salesforce, etc.). Each platform has a per-tenant custom-domain DKIM configuration that enables signing with d=<your-domain>. SPF alone is not enough — the moment a message gets forwarded once, SPF stops helping and DKIM is the only alignment path left.

Subdomain semantics under adkim=r and adkim=s

Two edge cases around subdomains deserve their own treatment: signatures from a SUBDOMAIN of From, and signatures from the PARENT of From.

From subdomain of d=

The common case: From is a subdomain, d= is the parent.

From:    alice@mail.example.com
d=       example.com

adkim=r: ALIGNED  (organizational domains both = example.com)
adkim=s: NOT ALIGNED (FQDNs differ)

Why: the From organizational domain (example.com) equals the d= organizational domain (example.com). Under relaxed, that match is sufficient. Under strict, the FQDN comparison fails because mail.example.com ≠ example.com.

d= subdomain of From

The mirror case: From is the parent, d= is a subdomain.

From:    alice@example.com
d=       mail.example.com

adkim=r: ALIGNED  (organizational domains both = example.com)
adkim=s: NOT ALIGNED (FQDNs differ)

Same outcome: relaxed aligns at the org- domain level; strict requires FQDN equality. Both directions of subdomain asymmetry are symmetric under relaxed.

Deep subdomains

From:    alice@example.com
d=       a.b.c.d.example.com

adkim=r: ALIGNED   (organizational domains both = example.com)
adkim=s: NOT ALIGNED

From:    alice@a.b.c.d.example.com
d=       example.com

adkim=r: ALIGNED   (organizational domains both = example.com)
adkim=s: NOT ALIGNED

Depth of subdomain nesting does not affect relaxed alignment — only the organizational-domain match matters.

The DMARC sp= tag interaction

A DMARC record at the organizational domain can specify a different policy for subdomains via the sp= tag. Subdomain alignment evaluation uses the SUBDOMAIN's effective adkim=, not the org-domain's directly. In practice the adkim= tag itself is inherited from the org-domain DMARC record when no subdomain-specific DMARC record exists. The DMARC policy rollout page covers the p=, sp=, np=, pct=, and related-tag interactions.

Different organizational domains

From:    alice@example.com
d=       example.org

adkim=r: NOT ALIGNED (different org domains)
adkim=s: NOT ALIGNED

Trivially never aligned. This is the canonical cross-organizational-domain signature that DMARC is designed to reject.

Common DKIM-alignment mistakes

  1. Treating dkim=pass as proof of DMARC pass. The single most common diagnostic confusion. dkim=pass means the signature verified cryptographically. DMARC pass additionally requires the d= to align with From:. Always inspect the aligned-signature column in your rua report, not just the verifier-verdict column.
  2. Leaving M365 tenant-default signing on. If Defender DKIM is not enabled for your custom domain, all outbound is signed with d=<tenant>.onmicrosoft.com — a verdict-pass alignment-fail situation. Enable custom-domain DKIM on EVERY tenant domain you send from (see section 7).
  3. Leaving Workspace Authenticate-email disabled. The Workspace counterpart of the M365 bug. Until Authenticate email is toggled on per sending domain, Google signs with the gappssmtp.com default (see section 8).
  4. Using adkim=s without subdomain-signing discipline. Strict alignment fails any subdomain-vs-parent mismatch. If you publish adkim=s for example.com but a transactional sender signs with d=mail.example.com, DMARC fails. Audit every sending source's actual d= before flipping to strict.
  5. Confusing i= with d=. RFC 7489 §3.1.1 specifies alignment against d=, not i=. Some receiver formats display header.i= in Authentication-Results but the domain portion of i= (after the @) is usually equal to d=. Look at the header.d= field directly when available.
  6. Not enabling custom-domain DKIM at SaaS senders. Every SaaS sender (SendGrid, Mailgun, Mailchimp, Postmark, HubSpot, Salesforce, Klaviyo, ConvertKit, etc.) has a custom-domain DKIM feature. Without it, the SaaS signs with its own platform domain (d=sendgrid.net, d=mailgun.org, etc.) — which never aligns with your From:.
  7. Ignoring the PSL effect on country-code TLDs. For example.co.uk, the organizational domain is example.co.uk, not co.uk. Operators publishing DMARC records at the second-level (co.uk) instead of the third-level (example.co.uk) get no policy enforcement because receivers look for the DMARC record at the organizational-domain level.
  8. Assuming forwarder-added signatures help. If a mailing list adds its own DKIM signature with d=<list-domain>, the signature can dkim=pass at receivers — but does not align with your From:. DMARC DKIM alignment for the list-forwarded message succeeds ONLY if the ORIGINAL signature (with d=<your-domain>) also survives. ARC sealing is the protocol-level fix when body modification breaks the original signature.
  9. Publishing DMARC at the wrong level for multi-TLD deployments. A company owning example.com and example.co.uk needs SEPARATE DMARC records at each organizational domain — the records do not inherit. Similarly DKIM keys published under example.com do not authenticate example.co.uk mail.
  10. Confusing the alignment mode with the policy mode. adkim=s (alignment is strict) and p=reject (policy is reject) are independent knobs. You can have adkim=r; p=reject — a forgiving alignment combined with an aggressive policy. Or adkim=s; p=none — strict alignment but monitor-only policy. The two tags do not constrain each other.

Troubleshooting matrix

Symptom → likely cause → next step:

SYMPTOM                              LIKELY CAUSE              NEXT STEP
====================================== ========================= ==========================
dkim=pass but DMARC fails              d= != From: org domain   Inspect header.d in
                                                                  Authentication-Results.
                                                                  If d=onmicrosoft.com or
                                                                  *.gappssmtp.com, enable
                                                                  custom-domain DKIM.

DMARC aggregate report shows           Some sender signing      Audit every SaaS sender's
mixed pass / fail percentages          with non-aligned d=      DKIM configuration.
for the same domain                                              Look for SaaS-default d=
                                                                  (sendgrid.net, mailgun.org,
                                                                  mandrill.io, etc.).

DMARC fails for mailing-list forwards  Body modification breaks Add ARC sealing OR accept
but passes for direct delivery         original signature       the mailing-list signing
                                                                  d= if it's a trusted list.
                                                                  Cannot align the list's
                                                                  d= with your From:.

DMARC fails for mail FROM your         d= is the parent;        Either: (a) sign with d=
subdomain From: with d= parent         adkim=s is published     equal to From: FQDN, or
                                                                  (b) relax to adkim=r.

dkim=permerror header.d=               Public-key TXT under     Check DNS for the
<your-domain>                          <s>._domainkey.<d>     selector record. Run
                                       missing or malformed     dig +short TXT
                                                                  <s>._domainkey.<d>

dkim=temperror                         Transient DNS lookup     Retry in 5-15 minutes.
                                       failure                  If persistent, check
                                                                  authoritative DNS health.

DMARC report rua shows                 Forwarder rewrote        Add ARC sealing if you
spf=fail dkim=fail for                 envelope AND body        control the forwarder;
forwarded mail                                                   else accept that forward
                                                                  losses are unavoidable
                                                                  without ARC.

DMARC fails for mail signed by         d= label includes        gappssmtp.com is on the
Workspace tenant default               *.gappssmtp.com          PSL. Enable custom-domain
                                                                  DKIM in Admin Console.

All Defender CNAMEs published          Defender DKIM toggle     Toggle Enabled = On at
but messages still d=onmicrosoft        not switched on         security.microsoft.com >
                                                                  DKIM page.

DMARC fails for SaaS-relayed mail      SaaS signing with its    Enable custom-domain DKIM
even though SaaS d= passes             own platform d=          at the SaaS provider
                                                                  control panel.

Frequently asked questions

How does DKIM alignment work in DMARC?
DKIM alignment compares the d= tag in the DKIM-Signature header against the domain in the From: header that the recipient sees. The comparison is per RFC 7489 §3.1.1. Under adkim=r (relaxed, the default), the organizational domains must match — mail.example.com and example.com align. Under adkim=s (strict), the d= value must equal the From: domain exactly. A DKIM signature aligns ONLY when it both verified cryptographically (dkim=pass) AND its d= matches the From: domain under the policy mode. dkim=pass alone is not enough for DMARC — the d= must align.
What is the difference between adkim=r and adkim=s?
adkim=r (relaxed, the default) requires the d= signing domain and the From: header domain to share an organizational domain — that is, they must agree at the registered-name + public- suffix level per the Public Suffix List. mail.example.com and example.com share organizational domain example.com so they align under relaxed. adkim=s (strict) requires d= to equal the From: domain byte-for-byte (case-insensitive ASCII) — mail.example.com and example.com do NOT align under strict. Strict is the more secure choice and is recommended for non-sending and high-value brand domains; relaxed is the practical default for active sending domains with cross-subdomain mail flows.
Why does dkim=pass not always mean DMARC passes?
dkim=pass means the signature verified cryptographically — the body hash matched the bh= tag and the signed-header set signature matched the b= tag. It does NOT mean the d= signing-domain identifier aligns with the From: header. A message can be dkim=pass with d=onmicrosoft.com and From: alice@yourcompany.com — the crypto is valid for the Microsoft tenant default signing identity, but it does not align with the From: header so DMARC fails on alignment. The verdict-vs-alignment distinction is the single most-misdiagnosed DMARC failure mode.
What is the onmicrosoft.com DKIM gotcha?
When you send mail through Microsoft 365 BEFORE enabling custom-domain DKIM via the Defender portal, Microsoft signs outbound mail with a tenant-default DKIM signature using d=<tenant>.onmicrosoft.com. The signature verifies cryptographically (dkim=pass at the receiver) — but d=onmicrosoft.com does NOT align with your From: header domain (yourcompany.com), so DMARC fails on DKIM alignment. The fix is to enable Defender DKIM CNAMEs for your custom domain so Microsoft signs with d=yourcompany.com instead. See help_dkim_for_microsoft_365.php for the full deployment procedure.
What is the gappssmtp.com DKIM gotcha?
When you send mail through Google Workspace BEFORE enabling custom-domain DKIM via the Admin Console, Google signs outbound mail with a default DKIM signature using d=<your-domain>.<tenant-id>.gappssmtp.com or a similar Workspace-default signing identity. The signature verifies (dkim=pass) but d=*.gappssmtp.com does NOT align with your From: header domain, so DMARC fails on DKIM alignment. The fix is to enable custom-domain DKIM in the Workspace Admin Console: Apps > Google Workspace > Gmail > Authenticate email > Generate new record, then publish the TXT and Start authentication. See help_dkim_for_google_workspace.php for the procedure.
Which DKIM signature drives alignment when a message has multiple?
A message can carry multiple DKIM-Signature headers — original sender + forwarder + mailing list each can sign with their own d=. The DMARC engine evaluates each signature independently for crypto-validity AND alignment, and DMARC passes DKIM if AT LEAST ONE valid signature aligns. RFC 7489 §3.1.1 wording: "any" verified signature whose d= aligns satisfies DKIM alignment. So a forwarder adding its own d=forwarder.example signature does not help (does not align), but it also does not hurt — the ORIGINAL sender signature with d=yourcompany.com is still present and still aligns. dkim=fail on a misaligned forwarder signature alongside dkim=pass on an aligned original signature still produces DMARC pass.
Why does DKIM alignment survive forwarding when SPF does not?
SPF authenticates the SMTP envelope MAIL FROM (Return-Path). When a forwarder receives a message and re-sends it, the forwarder rewrites MAIL FROM to its own bounce address — the original envelope domain is gone, so SPF on the forwarded message authenticates the forwarder, not the original sender, and SPF alignment fails. DKIM authenticates the d= tag, which is inside the signed-header set and travels with the message through forwards unmodified. As long as the forwarder does not modify the message body (which would break bh=) or strip the DKIM-Signature header (which would remove the signature entirely), the d= identifier survives forwarding and DKIM alignment still passes. This asymmetry is why every sending source should be DKIM-signed: DKIM is the resilient alignment path through real-world mail flow.
Does DKIM alignment use the i= AUID tag?
No. RFC 7489 §3.1.1 specifies that DMARC DKIM alignment compares the d= tag, NOT the i= AUID tag. The i= tag, when present, identifies a sub-agent or specific user within the signing domain — but DMARC ignores it for alignment purposes. So a signature with d=example.com and i=alice@example.com aligns identically to a signature with d=example.com and no i= tag, and aligns identically to a signature with d=example.com and i=@example.com. Some receivers compare i= against the From: header local-part for additional verification, but this is non-normative and not part of DMARC.

Check DKIM alignment in your reports

DMARC Analyzer parses every <auth_results><dkim> block in every uploaded aggregate report and surfaces the per-signature d= versus From: alignment in the dashboard. The Email Header Analyzer at header_analyzer.php accepts raw message headers and produces a parsed view of every DKIM-Signature instance including the alignment decision against the From: header. The Domain Security Checker validates your DMARC record's adkim= mode and confirms your DKIM selectors resolve to valid public-key TXT records.

Sign up — Free plan Inspect DKIM alignment