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 theDKIM-Signatureheader must match the visibleFrom:header domain. Defined in RFC 7489 §3.1.1. d=drives alignment, noti=. RFC 7489 §3.1.1 namesd=as the DKIM-side alignment identifier. Thei=AUID tag is ignored for alignment purposes (some receivers use it for non-normative additional checks).adkim=ris the default — organizational-domain match per the Public Suffix List.mail.example.comaligns withexample.com.adkim=srequires an exact match; same domains do NOT align under strict.dkim=pass≠ DMARC DKIM-aligned. A signature must verify cryptographically AND itsd=must align withFrom:. Theonmicrosoft.comandgappssmtp.comgotchas are exactly this distinction:dkim=passfor the tenant default but no alignment for the custom From: domain.- Multiple signatures, any aligns
wins. A message can carry many
DKIM-Signatureheaders; DMARC passes DKIM if AT LEAST ONE valid signature has an alignedd=. Forwarder-added signatures with their ownd=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 theDKIM-Signatureheader, 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.ukthe organizational domain isexample.co.uk, notco.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 ofd=and theFrom:header must match, determined via the Public Suffix List (next section).adkim=s— strict. Thed=value must equal theFrom: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 subdomaind=values (e.g.d=mail.example.comfor transactional,d=news.example.comfor 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 withd=<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:
- Find the longest sequence of labels at the end of the FQDN that matches an entry in the PSL. This is the public suffix.
- Prepend exactly ONE more label — the registered name.
- 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 customFrom:— you must use custom-domain DKIM withd=<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:
- Extract the
RFC5322.Fromdomain from the message — this is the SLD or FQDN in theFrom:header field that the user-facing client will display. - For each
DKIM-Signatureheader in the message, run the DKIM verifier per RFC 6376. Record each signature's result (pass,fail,none,neutral,policy,temperror,permerror) and thed=value. - For each signature with a
passresult, evaluate the DKIM identifier alignment against the From: domain using the policy mode (adkim=roradkim=s) from the published DMARC record for the From-domain organizational domain. - If ANY
passsignature has an alignedd=, DKIM alignment for DMARC = ALIGNED. - 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 withd=<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=20231101for receivers on the new key and one withs=20230501for slow DNS-cache receivers during a rotation window. - ARC sealing: the
ARC
chain adds
ARC-Message-Signatureheaders, which are NOTDKIM-Signatureheaders 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:
- 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=. - 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:
- Sign in to Microsoft Defender portal at security.microsoft.com.
- Navigate Policies & rules > Threat policies > DKIM under Rules.
- Select the custom domain in the table. Microsoft displays the two CNAME values for selector1 and selector2.
- Publish the two CNAME records at your
DNS provider —
selector1._domainkey.<your-domain>andselector2._domainkey.<your-domain>pointing at the values Microsoft showed. - 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:
- Sign in to the Admin Console at admin.google.com as a super-admin.
- Navigate Apps > Google Workspace > Gmail > Authenticate email.
- Select your sending domain. Click
Generate new record. Choose 2048-bit
key strength (per RFC 8301) and
accept the default selector
google. - 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. - After publication, return to the
Authenticate email page and click Start
authentication. Google begins signing
outbound with
d=<your-domain>s=googleon 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 byh=plus a body hash (bh=). As long as the forwarder does not modify the body (which would breakbh=) and does not strip theDKIM-Signatureheader, the original signature travels intact through the forward. Thed=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:
- 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. - 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. - Re-encoding of the body
Content-Transfer-Encoding —
relays that convert
quoted-printabletobase64or normalize line endings can change body bytes even when the "content" is semantically identical. Withc=relaxed/relaxedcanonicalization most line-ending normalization is tolerated; withc=simple/simpleit 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
- Treating
dkim=passas proof of DMARC pass. The single most common diagnostic confusion.dkim=passmeans the signature verified cryptographically. DMARC pass additionally requires thed=to align withFrom:. Always inspect the aligned-signature column in your rua report, not just the verifier-verdict column. - 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). - 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.comdefault (see section 8). - Using
adkim=swithout subdomain-signing discipline. Strict alignment fails any subdomain-vs-parent mismatch. If you publishadkim=sforexample.combut a transactional sender signs withd=mail.example.com, DMARC fails. Audit every sending source's actuald=before flipping to strict. - Confusing
i=withd=. RFC 7489 §3.1.1 specifies alignment againstd=, noti=. Some receiver formats displayheader.i=in Authentication-Results but the domain portion ofi=(after the@) is usually equal tod=. Look at theheader.d=field directly when available. - 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:. - Ignoring the PSL effect on
country-code TLDs. For
example.co.uk, the organizational domain isexample.co.uk, notco.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. - 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 (withd=<your-domain>) also survives. ARC sealing is the protocol-level fix when body modification breaks the original signature. - Publishing DMARC at the
wrong level for multi-TLD
deployments. A company
owning
example.comandexample.co.ukneeds SEPARATE DMARC records at each organizational domain — the records do not inherit. Similarly DKIM keys published underexample.comdo not authenticateexample.co.ukmail. - Confusing the alignment
mode with the policy mode.
adkim=s(alignment is strict) andp=reject(policy is reject) are independent knobs. You can haveadkim=r; p=reject— a forgiving alignment combined with an aggressive policy. Oradkim=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. Underadkim=r(relaxed, the default), the organizational domains must match —mail.example.comandexample.comalign. Underadkim=s(strict), thed=value must equal the From: domain exactly. A DKIM signature aligns ONLY when it both verified cryptographically (dkim=pass) AND itsd=matches the From: domain under the policy mode.dkim=passalone is not enough for DMARC — thed=must align. - What is the difference between adkim=r and adkim=s?
-
adkim=r(relaxed, the default) requires thed=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.comandexample.comshare organizational domainexample.comso they align under relaxed.adkim=s(strict) requiresd=to equal the From: domain byte-for-byte (case-insensitive ASCII) —mail.example.comandexample.comdo 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=passmeans the signature verified cryptographically — the body hash matched thebh=tag and the signed-header set signature matched theb=tag. It does NOT mean thed=signing-domain identifier aligns with the From: header. A message can bedkim=passwithd=onmicrosoft.comand 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=passat the receiver) — butd=onmicrosoft.comdoes 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 withd=yourcompany.cominstead. 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.comor a similar Workspace-default signing identity. The signature verifies (dkim=pass) butd=*.gappssmtp.comdoes 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 whosed=aligns satisfies DKIM alignment. So a forwarder adding its ownd=forwarder.examplesignature does not help (does not align), but it also does not hurt — the ORIGINAL sender signature withd=yourcompany.comis still present and still aligns.dkim=failon a misaligned forwarder signature alongsidedkim=passon 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 breakbh=) or strip the DKIM-Signature header (which would remove the signature entirely), thed=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 thei=AUID tag. Thei=tag, when present, identifies a sub-agent or specific user within the signing domain — but DMARC ignores it for alignment purposes. So a signature withd=example.comandi=alice@example.comaligns identically to a signature withd=example.comand noi=tag, and aligns identically to a signature withd=example.comandi=@example.com. Some receivers comparei=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.