Build1 publisher3 min readPublished
DMARC fails mail that passes SPF and DKIM under the wrong domain
Transactional mail can pass SPF and DKIM and still fail DMARC when neither aligns with the visible From domain, a dev.to Node.js guide shows. Because forwarding breaks SPF, the author treats aligned DKIM as the path to protect.
The Engineer · Build desk

What happened
- In the guide's example, a clinic proves control of notify.clinic.example during onboarding and still sends appointment reminders authenticated under a different domain.
- Two SPF TXT records published on the same domain invalidate each other, according to the guide.
- The guide judges each message by its Authentication-Results header, since a DNS dashboard cannot show a message rewritten during forwarding.
- DKIM selectors should be read from a real message's DKIM-Signature header, because guessing a name such as default returns clean output for the wrong key.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- decision Senders whose reminders get forwarded need DKIM signing under a d= domain that aligns with From, because forwarding breaks SPF and leaves an aligned DKIM signature as the remaining pass.
- exposure Publishing a DMARC record before either path aligns adds nothing and, per the guide, can worsen delivery by reporting the failures.
- constraint A passed ownership check cannot stand in for a deliverability check, since owning notify.clinic.example does not decide which domain signs each reminder.
DMARC checks one header against two identifiers. It takes the domain in the visible RFC5322 From header and compares it with whatever authenticated [2]. SPF counts only when the RFC5321 MailFrom domain aligns with From. DKIM counts only when the d= domain of a passing signature aligns [2]. One aligned pass is enough [2]. So a received header can show spf=pass for an unaligned bounce domain and dkim=pass for an unaligned signing domain, and the message still fails DMARC [3]. Of the clinic example, the author wrote: "Ownership passed. Alignment did not." [19]
The post tests failures in a deliberately dull order [7]:
1. Count SPF records. 2. Inspect a real received header. 3. Compare its identifiers with the visible From domain. 4. Compare public records with the intended control-plane state.
I'd copy the experiment that follows. Send a reminder straight to a controlled mailbox and record the From domain, the envelope domain reported for SPF, and every DKIM d= domain that passed. Then repeat with a forwarded copy [11]. If SPF aligns on the direct copy and fails on the forwarded one while the DKIM signature stays valid and aligned, the records agree with each other and the two delivery paths handed DMARC different evidence [11]. "That concrete split is why I choose DKIM as the alignment path to protect," the author wrote [12].
The post keeps the onboarding check and the deliverability check apart [20]. Onboarding must prove domain ownership before it completes. The deliverability check detects drift between intended records, published records and what a receiver authenticated [20]. "Treating those as one status creates a reassuring lie," the author wrote [15]. I think the split is right for an onboarding service, where a green ownership check is the status most likely to be mistaken for a deliverability result. The post stores intendedRecords, publishedRecords, messageAuthentication and ownershipStatus separately, and says a single dnsStatus field saves config at the cost of diagnostic value [14].
The companion TypeScript is honest about its scope. It queries the root TXT set and _dmarc through node:dns/promises, counts SPF records, calls the provider's /dns/record/list route with a Bearer key, and prints both evidence sets [16]. It does not try to compute a complete DMARC verdict, and it assumes nothing about the provider's response fields [9]. The retry loop has one sharp edge. On a 429 it sets retryAfter to Number(response.headers.get("retry-after")) and uses the 500 * 2 ** attempt fallback only when that value is not finite [17]. When the header is missing, get() returns null and Number(null) is 0, a finite number, so all three retries fire with no wait [1]. The backoff of 500, 1,000 and 2,000 ms, 3.5 seconds in all, applies only when the header arrives carrying something that is not a number [2].
The post does not measure how often each failure occurs. "A common cause" is the author's description, written for a healthtech onboarding service [1]. Its preference for DKIM rests on forwarding. The author says forwarding routinely breaks SPF [10]. For mail delivered straight to the recipient, an aligned SPF pass satisfies DMARC by itself [3]. The case for guarding DKIM grows with the share of recipients whose mail passes through a forwarder [3].
What to watch
- DMARC aggregate reports from a sending domain showing how often forwarded copies fail SPF alignment while DKIM holds; that would put a rate on the guide's 'routinely'.
- A revision of the script that treats a missing Retry-After header as absent, so 429 retries fall through to the 500 ms exponential backoff.