Mail can pass SPF and DKIM and still fail DMARC alignment, which is the failure that matters for the spam question. In a healthtech product that sends appointment reminders for a customer's domain, the fastest diagnosis is to compare two names in the delivered message: the visible From domain and the domain authenticated by SPF or DKIM. If neither authenticated name aligns with From, DMARC fails.
Short answer: inspect Authentication-Results, then trace the exact sending path. SPF alignment compares the RFC5321.MailFrom (the envelope sender) with the RFC5322.From domain. DKIM alignment compares the d= signing domain with From. A passing SPF check with a platform-owned bounce domain is not proof that the customer's visible domain is aligned.
The distinction is architectural, not cosmetic. A customer-owned zone can publish policy and authorize senders; a platform-owned zone can make delivery work while leaving the customer domain misaligned. Those choices change what you can prove, what you retain for investigation, and who can change DNS during an incident.
Which record actually aligns with the visible From domain?
Start with the message, not a DNS dashboard. Save the complete headers from a message that landed in spam and record these fields: From, Return-Path, the DKIM d= value, each spf= result, each dkim= result, and the dmarc= result. The domain strings are the evidence; a green status badge is only a summary.
DMARC alignment uses the organizational relationship between the visible From domain and an authenticated domain. RFC 7489 defines relaxed alignment as sharing an organizational domain, while strict alignment requires an exact domain match. The policy record is published at _dmarc.<From-domain>, so a policy on a platform domain does not govern mail whose visible From is the customer's domain.
Here is a deliberately small parser for a captured header. It does not replace a DMARC evaluator; it makes the two comparisons explicit during a review.
from dataclasses import dataclass
@dataclass
class AuthenticatedNames:
visible_from: str
spf_mailfrom: str | None
dkim_domain: str | None
def relaxed_parent(domain: str) -> str:
# Production code should use a Public Suffix List implementation.
labels = domain.lower().rstrip('.').split('.')
return '.'.join(labels[-2:]) if len(labels) >= 2 else domain.lower()
def aligned(authenticated: str | None, visible: str, strict: bool) -> bool:
if not authenticated:
return False
a = authenticated.lower().rstrip('.')
v = visible.lower().rstrip('.')
return a == v if strict else relaxed_parent(a) == relaxed_parent(v)
def dmarc_path(names: AuthenticatedNames, strict: bool = False) -> dict[str, bool]:
return {
"spf_aligned": aligned(names.spf_mailfrom, names.visible_from, strict),
"dkim_aligned": aligned(names.dkim_domain, names.visible_from, strict),
}
The Public Suffix List caveat is important. Splitting on the last two labels is useful for explaining the idea, but it is not a safe production algorithm for every suffix. Also check which DMARC mode is published with aspf and adkim; relaxed mode is the default when those tags are absent, while strict mode changes the boundary you must satisfy.
Why is mail going to spam after SPF and DKIM setup?
Because SPF answers a different question: did an authorized host send the envelope message? DMARC then asks whether that authenticated identity aligns with the visible sender. Consider a customer, clinic.example, using a healthtech platform. The message shows From: reminders@clinic.example, but Return-Path is bounce@mailer.platform.example. SPF can pass for mailer.platform.example. If the DKIM signature also uses d=platform.example, neither identity aligns with clinic.example; DMARC has no aligned pass.
A second trap is forwarding. A forwarder can preserve the visible From while changing the connecting host and envelope sender, so the original SPF result may not survive. DKIM often survives if the message is not modified, but a body or header change can invalidate the signature. A third trap is multiple DKIM signatures: one can fail and another can pass, and the passing signature still has to use an aligned d= domain.
The practical test is a two-column record for every sending route:
| Sending route | Authenticated identity to capture | Alignment question |
|---|---|---|
| Appointment reminders | envelope MailFrom and DKIM d=
|
Does either share the customer's organizational domain? |
| Password reset mail | envelope MailFrom and DKIM d=
|
Is the identity the same in staging and production? |
| Support or billing notices | every DKIM d= value |
Did a second sender bypass the documented DNS change? |
Do not infer alignment from the DNS records alone. DNS tells you what a receiver should evaluate; the received header tells you what it actually evaluated.
One header is enough to disprove a comforting dashboard.
Customer-owned or platform-owned zone: what do you retain?
The ownership decision determines the audit trail. With a customer-owned zone, the customer publishes the SPF authorization, DKIM public key, and DMARC policy. Your system should retain the selector, the intended d= domain, the DNS change request, and the activation timestamp. You can then compare a message header with the exact configuration that was active when it was sent.
With a platform-owned zone, the platform controls those records and can offer a stable envelope sender. That can simplify operations, but the visible customer domain still needs an aligned DKIM signature or an aligned SPF MailFrom if DMARC is expected to pass for that domain. A platform-owned policy cannot grant authority over a customer-owned _dmarc label.
Retention has a cost, and this is where teams often make a false economy. Keep the full authentication header and DNS configuration history long enough to correlate delayed aggregate reports and customer support tickets; discard message bodies and recipient addresses unless they are needed for a documented clinical or security purpose. What you deliberately stop keeping is the payload. What you should not stop keeping is the small set of identities that explains the decision.
I would also record negative evidence: a route that has no aligned DKIM signature, a selector that resolves to NXDOMAIN, or a DMARC policy inherited from a parent organizational domain. Those entries make a later change review much faster than a screenshot of a dashboard.
A debugging sequence that survives handoffs
First, capture one spammed message and one inboxed message from the same route. Compare From, Return-Path, DKIM d=, selector, and DMARC result. Second, query the customer domain's _dmarc record and document p, adkim, and aspf; do not assume the default because an explicit tag may be present. Third, query every DKIM selector named in the headers and verify that the public key is present at <selector>._domainkey.<d=>.
Fourth, inventory all senders: reminders, resets, invoices, and human support tools frequently use different envelope domains. Fifth, change one route at a time and send a controlled message whose recipient can provide raw headers. Sixth, watch aggregate reports for the source IP and aligned identifiers before moving a policy from monitoring to enforcement.
Failure modes deserve names because they recur:
-
Envelope-only success: SPF passes for a platform domain, but no identity aligns with
From. - Selector drift: production signs with a selector that was never published in the customer's zone.
- Policy shadowing: a subdomain has no local policy and inherits the organizational policy, surprising an owner who checked the wrong label.
- Forwarding breakage: the forwarder changes the path, causing SPF failure while DKIM may or may not survive.
- Split ownership: one team edits DNS while another changes the signing domain, so the records and headers describe different systems.
A useful runbook ends with an owner and a rollback condition, not just a green test. For example: keep p=none while all legitimate routes are inventoried, require an aligned DKIM result for each route, and only then tighten policy after report data shows no unexplained sources. The exact policy change belongs to the domain owner and the organization's risk process.
What the alignment result can and cannot tell you
DMARC alignment is an identity check, not an inbox-placement guarantee. A message can pass DMARC and still be filtered for reputation, content, user complaints, or receiver-specific rules. Conversely, a spam placement does not prove that DNS is wrong. It proves that you need the receiver's authentication result and the route's evidence before changing records.
For a healthtech system, that distinction protects two boundaries at once: the customer controls the domain's publishing authority, and the platform controls only the sending path it actually operates. Keep those boundaries visible in configuration, headers, and retention. The record that matters is the aligned identity in the received message, not the record that merely looks correct in a DNS editor.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.