A single email carries three separate domain identities, plus the IP address it was actually delivered from, and nothing requires any of them to match. Most of the confusion around SPF, DKIM and DMARC comes from assuming there is one "sender" to check. There are three identities, they are checked by different mechanisms, and a message can pass DMARC while two of the three point in different directions.
Here is a constructed example (not a real customer message, but a realistic and common configuration) that shows all of them at once: an invoice email sent through an email service provider (ESP), the way most SaaS billing systems actually send mail.
The identities in one message
The mail client shows a customer of acme-example.com an invoice from "Acme Billing". Underneath, these values determine whether that message authenticates:
Return-Path: <bounces+4471-acmecorp=acme-example.com@bounce.esp-example.net>
Received: from mta7.esp-example.net (mta7.esp-example.net [198.51.100.23])
From: "Acme Billing" <billing@acme-example.com>
DKIM-Signature: v=1; a=rsa-sha256; d=acme-example.com; s=s2; ...
Authentication-Results: mx.recipientdomain.com;
spf=pass (sending IP 198.51.100.23) smtp.mailfrom=bounce.esp-example.net;
dkim=pass header.d=acme-example.com header.s=s2;
dmarc=pass (p=REJECT sp=REJECT) header.from=acme-example.com
Three domain identities and one supporting IP, one message:
- Visible From:
billing@acme-example.com, read from theFromheader. This is the only identity a human looks at, and the only one DMARC cares about aligning to. - Envelope sender:
bounce.esp-example.net, read fromReturn-Path(equivalently, the SMTPMAIL FROM). This is where bounces go, and it belongs to the ESP, not Acme. SPF checks this domain, not the visible From. - DKIM domain:
acme-example.com, read fromd=in theDKIM-Signatureheader. Acme configured a custom signing domain with its ESP rather than using the ESP's default. This is a separate, independent identity from both the envelope and the visible From. - Sending IP:
198.51.100.23, a shared IP on the ESP's platform, read from theReceivedheader. This is not a domain identity; it is the delivery source. SPF's job is to check whether this IP is authorised to send for the envelope domain, not foracme-example.comat all.
How the checks actually run
SPF (RFC 7208) asks one question: is sending IP 198.51.100.23 authorised to send for the envelope domain, bounce.esp-example.net? The ESP publishes an SPF record for that subdomain listing its own sending IPs, so the answer is yes. acme-example.com does not appear in this check anywhere. DMARC uses the SPF-authenticated MAIL FROM identity for alignment, so it is the envelope domain, not the sending IP itself, that ever gets compared to anything.
DKIM (RFC 6376) asks a different question: does the cryptographic signature in DKIM-Signature verify against the public key published at s2._domainkey.acme-example.com? Acme generated that keypair when they set up custom DKIM with its ESP, so it does. This check never looks at the envelope or the sending IP either. It only proves that the signed portions of the message (the header fields and body content actually covered by the signature) match what the holder of the private key for acme-example.com signed, and were not altered in transit. Fields excluded from the signature are not protected by it.
DMARC (RFC 9989) does not add a third check. It takes whichever of those two already produced a result and asks whether that result's domain aligns with the visible From domain, acme-example.com. Here, SPF's authenticated MAIL FROM domain is bounce.esp-example.net, which does not align with acme-example.com under any DMARC alignment mode, so the SPF leg of DMARC fails despite SPF itself passing. DKIM's authenticated domain is acme-example.com exactly, an identical match under both relaxed and strict alignment, so the DKIM leg passes. DMARC only needs one aligned pass, so the message passes DMARC on DKIM alone. This is why the header block above shows spf=pass next to a domain that is not Acme's, and dmarc=pass anyway.
flowchart TD
M[Email] --> V[Visible From: acme-example.com]
M --> E[Envelope sender: bounce.esp-example.net]
M --> K[DKIM d=: acme-example.com]
M --> I[Delivery IP: 198.51.100.23]
E --> SPF{SPF authenticates envelope}
I --> SPF
SPF -->|pass, but envelope does not align to From| SPFALIGN[SPF leg: fails alignment]
K --> DKIM{DKIM signature verifies}
DKIM -->|pass, d= matches From exactly| DKIMALIGN[DKIM leg: passes alignment]
SPFALIGN --> DMARC{Either leg aligned?}
DKIMALIGN --> DMARC
DMARC -->|yes, DKIM leg| PASS([DMARC pass])
The same message, evaluated on two independent legs. SPF passes but does not align. DKIM passes and aligns. DMARC needs only one.
Where this goes wrong
Swap one detail in this example and the outcome flips. If Acme had never configured custom DKIM with its ESP, the message would carry the ESP's default signature with d=esp-example.net instead. That signature would still verify, the ESP still legitimately sent the mail, but esp-example.net does not align with acme-example.com either. Now neither leg aligns, and an entirely genuine invoice fails DMARC. It is one way a real ESP integration breaks DMARC: not a forged message, a correctly authenticated one whose only aligned identity was never set up.
It looks identical from the mail client's side, a legitimate email, sent through a paid-for legitimate provider, landing in spam or getting rejected. The difference is entirely in d=, a field the sender's marketing team never sees and has no reason to know exists. Diagnosing it means reading the DKIM-Signature header directly, or, in a DMARC aggregate report, the DKIM authentication result's domain field, and checking it against the organisational domain in identifiers/header_from, not against the sending IP or the envelope, which will both look fine.
An aggregate report (RFC 9990) actually carries two separate answers, and mixing them up is where most people misread this. The auth_results element records the underlying SPF and DKIM authentication outcomes on their own terms, uninterpreted with respect to DMARC: auth_results/dkim/domain is the d= value that was validated, and auth_results/spf/domain is the MAIL FROM domain that was checked. The separate policy_evaluated element records whether those results aligned with the visible From domain for DMARC purposes: policy_evaluated/spf and policy_evaluated/dkim. Before custom DKIM is configured, a report record for that ESP's source IP can show both underlying checks passing in auth_results (SPF against the ESP's envelope domain, DKIM against the ESP's own signing domain) while policy_evaluated shows both spf and dkim as fail, because neither authenticated domain aligns with acme-example.com. After custom DKIM is configured, auth_results/dkim/domain reads acme-example.com, policy_evaluated/dkim flips to pass, and the overall disposition follows it. Nothing about the sending IP or volume changes between the two, only the signing domain the DKIM check actually validated.
Relaxed versus strict alignment
Both legs in this example were checked under DMARC's default relaxed alignment, where a subdomain counts as aligned with its parent organisational domain. A DKIM signature from d=mail.acme-example.com would still align with a From of acme-example.com under relaxed mode. Set adkim=s or aspf=s in the DMARC record and alignment tightens to an exact match, and that same subdomain signature would then fail. Most senders should stay on relaxed unless they have a specific reason not to: strict mode breaks legitimately structured mail (subdomains signing on behalf of the parent) more often than it stops anything, since an attacker who controls a lookalike domain is unaffected by either mode.
Checking your own ESP for this
If you run a domain that sends through any ESP, marketing platform or transactional mail service, check whether that provider is signing with your domain in d= or with its own. If it is signing with its own, your mail authenticates for the provider, not for you, and DMARC will only pass if SPF happens to align instead, which many ESP setups do not achieve either. The fix is configuring custom DKIM (most ESPs call it "domain authentication" or "branded links and domains") so d= carries your domain. Check this before moving that sender's policy anywhere near p=reject: the sender inventory in a DMARC aggregate report will show you the sending source, but only the raw DKIM-Signature header, or the DKIM authentication result's domain field in the report itself, tells you which identity actually aligned.
See which of your senders actually align, and which only look like they do.
SenderLedger breaks down SPF and DKIM alignment per sending source, so you can find the ESP that never got custom DKIM before it costs you real mail.
Request access