Microsoft 365 `compauth` reason codes explained: when SPF, DKIM, or DMARC are not the whole story

microsoft 365dmarc

If you troubleshoot mail into Microsoft 365 long enough, you eventually hit a header that makes the normal SPF, DKIM, and DMARC story feel incomplete:

Authentication-Results: ... compauth=pass reason=130

Or this one:

Authentication-Results: ... dmarc=fail action=none header.from=example.com; compauth=pass reason=109

Or the opposite:

Authentication-Results: ... spf=pass; dkim=pass; compauth=fail reason=001

That is the point where many admins realize Microsoft is evaluating more than the three familiar acronyms alone.

In Microsoft 365, compauth means composite authentication. Microsoft combines regular authentication results with other signals to decide whether the message looks legitimate, spoofed, or trustworthy enough to treat gently. Microsoft Learn explicitly says this evaluation can include SPF, DKIM, DMARC, sender reputation, sender history, recipient history, behavioral analysis, and other advanced techniques.

So if a message verdict feels odd, compauth is often the missing layer.

Short answer

compauth is Microsoft's combined authentication verdict for inbound mail. The reason= value explains why Microsoft 365 marked the message as pass, softpass, fail, or none.

The practical meaning is this:

  • compauth=pass does not always mean DMARC passed
  • compauth=fail does not always mean the message will be blocked
  • some reason codes reflect direct DMARC enforcement
  • others reflect implicit trust, ARC override, PTR or MX alignment hints, prior good history, or bypass scenarios

If you only read spf=, dkim=, and dmarc=, you can miss the real explanation for Microsoft's final handling.

Microsoft documents the current reason code table in its Anti-spam message headers, Email authentication, and SecOps guide for email authentication pages.

What compauth is really doing

DMARC is standards-based and relatively strict: either aligned SPF or aligned DKIM passes, or DMARC fails.

Microsoft's compauth layer is different. It tries to answer a more operational question:

given the full context of this message, should Microsoft treat the sender identity as trustworthy, suspicious, or inconclusive?

That is why messages can land in combinations that surprise people:

  • DMARC fails, but compauth passes because ARC from a trusted sealer preserved trustworthy original authentication
  • no DMARC record exists, but compauth passes because the message still aligns well enough with SPF or DKIM for Microsoft to accept it implicitly
  • SPF and DKIM both look individually fine, but the message still fails composite auth because the authenticated domains do not support the visible From: identity
  • DMARC is not enforced because Microsoft recognizes a valid NDR, complex routing path, or previously trusted infrastructure history

If this sounds close to the broader point in Why DMARC passes at one provider and fails at another, that is because it is. Microsoft is one of the clearest examples of a receiver applying a local trust model on top of the standards.

The four compauth result families

Microsoft stamps one of four top-level values:

compauth=pass

Microsoft decided the message authenticated strongly enough overall.

This can happen because:

  • DMARC passed in the normal standards sense
  • SPF alignment or DKIM alignment was strong enough even without an explicit DMARC record
  • ARC from a trusted sealer overrode a DMARC failure
  • Microsoft had enough additional trust signals to accept the message identity

compauth=softpass

Microsoft saw some legitimacy signals, but not enough for a full confident pass.

This usually appears with the 2xx reason family and often points to weaker implicit trust, such as PTR-domain relationships rather than strong explicit standards-based alignment.

compauth=fail

Microsoft concluded the sender identity was not trustworthy enough.

That might be because:

  • DMARC failed under an enforcing policy
  • the domain published weak or missing auth records and did not earn implicit trust
  • the message looked like spoofing of your own accepted domains
  • a manual policy explicitly prohibited the sender/domain pairing

Important detail: Microsoft says a compauth failure does not automatically mean the message is blocked. Other filtering layers still matter.

compauth=none

Composite authentication was not checked or was bypassed.

This usually appears in the 3xx, 4xx, or some 9xx situations, where another routing or policy condition made normal composite evaluation irrelevant.

The reason codes that matter most in practice

Microsoft publishes many codes, but a smaller subset explains most real troubleshooting cases.

reason=000

This is the straightforward hard-fail case.

Microsoft says 000 means the message failed explicit authentication and received a DMARC fail where the policy action is p=quarantine or p=reject.

Operationally, read this as:

  • DMARC failed in the normal standards sense
  • the domain owner published an enforcing policy
  • Microsoft did not find a reason to rescue the message at the composite-auth layer

If you see 000, start with ordinary DMARC troubleshooting: compare header.from, smtp.mailfrom, and header.d, then fix alignment. If you want a refresher, DMARC troubleshooting with real headers and Return-Path vs From cover that workflow.

reason=001

This is one of the most useful and misunderstood codes.

Microsoft says 001 means the message failed implicit authentication because the sending domain had no published authentication records, or only weak policy such as SPF ~all, SPF ?all, or DMARC p=none.

This matters because it explains a common admin reaction:

“But SPF passed” or “DKIM passed”

That can still coexist with compauth=fail reason=001 if the pass does not actually support the visible From: identity strongly enough, or if the overall sender posture remains too weak for Microsoft to trust.

In other words, 001 often means the standards did not fail in the simplest binary sense, but the sender still did not look convincingly authenticated.

Typical causes:

  • no DMARC record at all
  • DMARC exists but stays at p=none
  • SPF passes only for a different envelope domain that does not align with From:
  • DKIM passes with a signing domain that does not align with From:
  • the message resembles spoofing and lacks stronger counter-signals

reason=010

Microsoft documents 010 as a DMARC-fail situation with p=reject or p=quarantine where the sending domain is one of your organization's accepted domains.

This is the self-spoofing or intra-org spoofing case.

When you see 010, do not treat it like an ordinary third-party deliverability issue. Treat it as one of these instead:

  • a device or app is sending as your domain through the wrong path
  • a partner is spoofing your visible domain without authorization
  • internal routing is bypassing the signing/alignment path you expected

reason=109

This is the code that often makes people say “wait, DMARC passed even though there is no DMARC record?”

Strictly speaking, Microsoft says 109 means the sender's domain has no DMARC record, but the message would pass anyway.

You will often see this with dmarc=bestguesspass rather than an actual policy-backed DMARC pass.

The right interpretation is:

  • the sender still needs to publish real DMARC
  • Microsoft is effectively saying the message looks like it would have passed if DMARC existed
  • this is better than total failure, but it is not a mature sender posture

If you own the domain, fix it by publishing a real DMARC record. DMARC policy modes explained is a good companion if the sticking point is rollout policy.

reason=111 and reason=112

These two are easy to overread.

Microsoft says:

  • 111: despite a DMARC temp/permanent error, SPF or DKIM aligns with the From: domain
  • 112: a DNS timeout prevented the DMARC record from being retrieved

These codes tell you the message outcome may reflect DNS retrieval trouble, not a clean sender-side authentication design.

If 111 appears repeatedly, verify the DNS health and syntax of the sender's DMARC record anyway. If 112 appears repeatedly for the same domains, suspect lookup instability, timeouts, or infrastructure in front of Microsoft that complicates retrieval.

reason=115

Microsoft says 115 means the message was sent from a Microsoft 365 organization where the From: domain is configured as an accepted domain.

This tends to show up in Microsoft-native flows where Microsoft has a strong platform-level basis to trust the sender identity.

It is less about “what exact public DMARC evidence convinced the receiver?” and more about “Microsoft knows this domain belongs to a tenant sending through expected Microsoft 365 paths.”

reason=130

This is the famous ARC override case.

Microsoft says 130 means the ARC result from a trusted ARC sealer overrode the DMARC failure.

This is the code to look for when:

  • forwarding should have broken SPF
  • an intermediary modified the body and broke DKIM
  • DMARC failed, but Microsoft still treated the message as legitimate

If you are dealing with secure email gateways, list expanders, or forwarding services, 130 often tells the whole story. Microsoft documents this behavior in Configure trusted ARC sealers.

For broader background, see What is ARC and how does it work? and Trusted ARC sealers at mailbox providers.

reason=501 and reason=502

These are special-case NDR or bounce scenarios.

Microsoft says:

  • 501: DMARC was not enforced because the message is a valid NDR and sender-recipient contact is previously established
  • 502: DMARC was not enforced because the message is a valid NDR for mail sent from this organization

If you are looking at DSNs, bounces, or null return-path traffic, these codes matter a lot. Regular DMARC expectations do not map cleanly onto that traffic class. That is exactly why DMARC for NDRs, DSNs, and auto-replies exists.

reason=601

Microsoft says 601 means the sending domain is an accepted domain in your organization, again pointing to self-to-self or intra-org spoofing.

Think of 601 as another “this is your own domain problem, not an outside sender problem” flag.

reason=701 to 704

These are easy to miss, but they explain a lot of “why did Microsoft still let this through?” incidents.

Microsoft says these codes mean DMARC was not enforced because the recipient organization has a history of receiving legitimate messages from the sending infrastructure.

This is one of the clearest examples of Microsoft applying local trust on top of standards. The message might not satisfy the kind of strict textbook expectation an admin has in mind, but Microsoft has enough historical confidence in the infrastructure to avoid treating it as straightforward spoofing.

That is useful to know diagnostically, but it should not be read as permission to leave the sender misconfigured. Receiver trust history is not portable across providers.

reason=905

Microsoft says 905 means DMARC was not enforced because of complex routing, for example when internet mail passes through on-premises Exchange or a non-Microsoft service before reaching Microsoft 365.

This is a big one for hybrid and gateway-heavy environments.

If you see 905, ask these questions immediately:

  • is MX pointing directly at Microsoft 365?
  • does a third-party gateway sit in front?
  • is Enhanced Filtering for Connectors configured correctly?
  • is an intermediary modifying headers or bodies?
  • should ARC trust be configured?

When admins say “the sender is fine but Microsoft is doing something weird,” 905 often turns out to mean your routing topology is part of the authentication result.

How to read compauth without fooling yourself

The safest reading order is:

  1. read header.from= first because Microsoft says compauth uses the visible From: domain as its basis
  2. read spf=, smtp.mailfrom=, dkim=, and header.d=
  3. read dmarc= and the DMARC action=
  4. then read compauth= and reason= as Microsoft's final interpretation layer

That order matters because compauth is not a replacement for the underlying evidence. It is Microsoft's summary of how that evidence should be interpreted in context.

A few common real-world patterns

Pattern 1: dmarc=fail but compauth=pass reason=130

Interpretation: forwarding or message modification likely broke normal authentication, and ARC from a trusted sealer preserved enough trust to rescue the message.

Pattern 2: dmarc=bestguesspass and compauth=pass reason=109

Interpretation: the sender has no DMARC record, but SPF or DKIM behavior still gives Microsoft enough confidence to treat the message as implicitly acceptable.

Fix: publish real DMARC anyway.

Pattern 3: spf=pass, dkim=pass, but compauth=fail reason=001

Interpretation: do not celebrate the raw passes too early. Check whether the authenticated domains actually support the visible From: identity. This is often an alignment or spoofing-context problem, not a missing-check problem.

Pattern 4: compauth=none reason=905

Interpretation: complex routing or a non-standard path made Microsoft's normal composite-auth logic less applicable. Investigate the recipient-side mail path, not just the sender's DNS.

Pattern 5: compauth=fail reason=010 or 601

Interpretation: Microsoft thinks this is spoofing involving one of your own accepted domains. Look inward first.

What compauth does not tell you by itself

compauth is useful, but it is not magic.

It does not, by itself, tell you:

  • whether the sender's DNS is correctly designed overall
  • whether another mailbox provider will make the same decision
  • whether the message was spam-filtered for content reasons unrelated to auth
  • whether a third-party gateway broke DKIM after receipt
  • whether your policy overrides or allow lists changed the final disposition

So always pair compauth with the rest of the header and the surrounding mail flow.

Practical troubleshooting order

When a Microsoft 365 result feels contradictory, use this order:

  1. confirm the visible From: domain you are actually trying to authenticate
  2. check raw SPF, DKIM, and DMARC results
  3. check domain alignment, not just pass/fail words
  4. inspect compauth and reason
  5. inspect ARC headers if forwarding or modification is involved
  6. inspect routing complexity, especially gateways or hybrid paths in front of Microsoft 365
  7. only then decide whether the problem is sender auth, recipient routing, or Microsoft trust logic

That order prevents a lot of wasted time.

Bottom line

Microsoft 365 compauth reason codes exist because real inbound mail is messier than a pure SPF/DKIM/DMARC checklist.

If you remember only one thing, make it this: compauth is Microsoft's explanation of how it interpreted the message identity after looking beyond the bare standards results.

That means:

  • 000 and 001 usually explain why Microsoft was not persuaded
  • 109 explains why a sender with no DMARC record still got some trust
  • 130 explains why ARC changed the story
  • 501 and 502 explain why NDR traffic is special
  • 701 to 704 explain why prior good history can matter
  • 905 explains why complex routing can change the evaluation entirely

Once you start reading those codes as context clues rather than mysterious verdicts, Microsoft header analysis gets much easier.

Previous Post