24 September 2026 Updated: 25 October 2026 16 min read

MailHeaderAnalyzer: Analyze email headers in PowerShell without network access

Reference for the PowerShell module MailHeaderAnalyzer: parameter overview, syntax, description, examples, and parameter properties for Get-MailHeaderAnalysis and ConvertTo-MailHeaderReport, plus the output object with delivery chain, authentication results, Exchange Online classification, and all finding codes.

MailHeaderAnalyzer is a PowerShell module with two cmdlets. Get-MailHeaderAnalysis analyzes an email header: delivery chain with delays and TLS details, SPF, DKIM, DMARC, and ARC results including origin verification against your gateway’s authserv-id, DMARC alignment, Exchange Online hybrid classification, assessments from Microsoft Defender, SpamAssassin, and Rspamd, as well as anomalies such as duplicate From lines or Unicode control characters. ConvertTo-MailHeaderReport creates a report for tickets. The module works entirely offline: no DNS queries, no HTTP connections. It is the command-line version of the Header Analyzer on this website and uses the same analysis logic.

ModuleMailHeaderAnalyzer in the PowerShell Gallery
Source codepfstr/MailHeaderAnalyzer on GitHub, MIT license
Applies toWindows PowerShell 5.1, PowerShell 7.x; Windows, Linux, macOS; Exchange Management Shell
CmdletsGet-MailHeaderAnalysis, ConvertTo-MailHeaderReport

Parameter overview

CmdletParameterTypeRequiredPipelineEffect
Get-MailHeaderAnalysis-HeaderString[]Yes (Text parameter set)Yes, by valueThe header as text. Lines from the pipeline are assembled into one header
Get-MailHeaderAnalysis-PathString[]Yes (Path parameter set)Yes, by property nameFile containing the header or a complete .eml message; accepts objects from Get-ChildItem
Get-MailHeaderAnalysis-FromClipboardSwitchYes (Clipboard parameter set)NoReads the header from the clipboard (Windows only)
Get-MailHeaderAnalysis-TrustedAuthServIdString[]NoNoauthserv-id(s) of your inbound gateway; only authentication lines with one of these IDs are considered evidenced
ConvertTo-MailHeaderReport-AnalysisMailHeaderAnalyzer.AnalysisYesYes, by valueThe result object from Get-MailHeaderAnalysis
ConvertTo-MailHeaderReport-FormatStringNoNoMarkdown (default) or Text

Both cmdlets support the common parameters -Verbose, -ErrorAction, -ErrorVariable, -OutVariable and the others from about_CommonParameters.

Installation

Install-Module -Name MailHeaderAnalyzer -Scope CurrentUser
OptionEffect
-Name MailHeaderAnalyzerModule name in the PowerShell Gallery
-Scope CurrentUserInstalls to the user’s module directory, without administrator rights

On a system without internet access, download the module on another computer with Save-Module -Name MailHeaderAnalyzer -Path C:\Temp and copy the MailHeaderAnalyzer folder to a directory from $env:PSModulePath, for example %USERPROFILE%\Documents\WindowsPowerShell\Modules (Windows PowerShell 5.1) or %USERPROFILE%\Documents\PowerShell\Modules (PowerShell 7). Update-Module -Name MailHeaderAnalyzer retrieves updates, and Get-Module -Name MailHeaderAnalyzer -ListAvailable displays the installed version.

Get-MailHeaderAnalysis

Analyzes an email header and returns an analysis object.

Syntax

Text (default)

Get-MailHeaderAnalysis
    [-Header] <String[]>
    [-TrustedAuthServId <String[]>]
    [<CommonParameters>]

Path

Get-MailHeaderAnalysis
    -Path <String[]>
    [-TrustedAuthServId <String[]>]
    [<CommonParameters>]

Clipboard

Get-MailHeaderAnalysis
    -FromClipboard
    [-TrustedAuthServId <String[]>]
    [<CommonParameters>]

Description

The cmdlet parses the raw header into fields, unfolds RFC 5322 folding, and decodes RFC 2047 values in the subject and addresses. From the Received lines, it builds the delivery chain in chronological order, calculates the delay at each hop, and reads the TLS version, cipher, and protocol class according to RFC 3848. From Authentication-Results, Received-SPF, DKIM-Signature and the ARC chain, it determines authentication results and assigns an origin to each authentication line (RFC 8601, section 5): evidenced if its authserv-id is listed in -TrustedAuthServId, otherwise only plausible or unsubstantiated; see AuthTrust. It also includes DMARC alignment, Exchange Online hybrid classification, spam filter assessments, and a list of anomalies.

The cmdlet performs no DNS queries and opens no network connection. Spf, Dkim, Dmarc and Arc are therefore always the receiving server’s verdict. DKIM and ARC signatures are not cryptographically verified; for the ARC chain, the cmdlet checks only its structure (ArcStructure).

Input is read tolerantly: a blank line ends the header, and any following message body is ignored. Lines without a field name and without leading whitespace, as can result from copying from client dialogs, are assigned to the preceding field. An mbox separator line From ... before the first field is skipped, and a byte order mark is removed. At most 200 Received lines are analyzed, counted from delivery onward.

Examples

Example 1

Get-MailHeaderAnalysis -FromClipboard

Analyzes the header currently in the clipboard. In Outlook for Windows, you can find the header under File, Properties, Internet headers; in Outlook on the web, under “View message details” in message options.

Subject     : Service-Report für März
From        : Beispiel Newsletter <news@example.org>
Date        : 2026-08-03 09:14:27Z
SPF         : pass
DKIM        : pass
DMARC       : pass
ARC         : -
CompAuth    : pass (reason 100)
AuthTrust   : Absent (no authserv-id, Microsoft 365 style)
Hops        : 3 (total 00:00:42)
DeliveredBy : zr0p278mb0570.chep278.prod.outlook.com
Findings    : none

Example 2

Get-MailHeaderAnalysis -Path .\nachricht.eml

Analyzes a saved message. The file may contain only the header or the complete message; the message body is ignored.

Example 3

(Get-MailHeaderAnalysis -Path .\nachricht.eml).Hops

Displays the delivery chain as a table, with the first hop first.

#   From                 IP              By                                     Protocol   TLS      Time (UTC)           Delay
-   ----                 --              --                                     --------   ---      ----------           -----
1   client.example.net   198.51.100.34   mail.example.org                       ESMTPSA    -        2026-08-03 09:14:28  -
2   mail.example.org     203.0.113.25    mx.eur02.prod.protection.outlook.com   Microsoft… TLS 1.3  2026-08-03 09:15:09  41 s
3   AM0EUR02FT056.eop…   -               ZR0P278MB0570.CHEP278.PROD.OUTLOOK.COM Microsoft… TLS 1.2  2026-08-03 09:15:10  1 s

Example 4

Get-Content -Path .\header.txt | Get-MailHeaderAnalysis | Select-Object -ExpandProperty Findings

Reads the header from a text file line by line and displays only the anomalies. -Raw with Get-Content is not required; the cmdlet assembles the lines itself.

Example 5

Get-ChildItem -Path .\export\*.eml |
    Get-MailHeaderAnalysis |
    Select-Object Source, Subject, Spf, Dkim, Dmarc, AuthTrust, HopCount,
        @{ Name = 'Findings'; Expression = { ($_.Findings.Code | Sort-Object -Unique) -join ',' } } |
    Export-Csv -Path .\auswertung.csv -NoTypeInformation -Encoding UTF8

Analyzes all messages in a folder and writes a CSV file with one row per message. The calculated column combines the finding codes.

OptionEffect
Get-ChildItem -Path .\export\*.emlReturns the file objects; -Path accepts their FullName property
Select-Object … @{ Name; Expression }Calculated column that combines all finding codes into one string
Export-Csv -NoTypeInformation -Encoding UTF8CSV without a type header, UTF-8 for umlauts in subject lines

Example 6

Get-ChildItem .\export\*.eml |
    Get-MailHeaderAnalysis -TrustedAuthServId 'mx.example.org' |
    Where-Object AuthTrust -ne 'Trusted' |
    Select-Object Source, AuthTrust, AuthServId, DeliveredBy

Lists messages whose authentication results do not originate from your own inbound gateway mx.example.org. For phishing analysis, this is an initial filter. Without -TrustedAuthServId, you can filter only for Unmatched; a forgery that also includes a matching Received line then appears as Matched and passes through the filter.

Options explained
OptionEffect
-TrustedAuthServId 'mx.example.org'authserv-id that your gateway writes in Authentication-Results; exact comparison, without subdomains
Where-Object AuthTrust -ne 'Trusted'Keeps all messages whose authoritative authentication line has no trusted authserv-id
Select-Object Source, AuthTrust, AuthServId, DeliveredByFile, origin level, authserv-id of the authentication line, and delivering hop

Example 7

$a = Get-MailHeaderAnalysis -Path .\nachricht.eml
$a.Hops[$a.SlowestHopIndex - 1] | Format-List Index, FromHost, ByHost, Delay

Displays the hop with the greatest delay. SlowestHopIndex is 1-based; the Hops array is 0-based.

Example 8

Get-MailHeaderAnalysis -Path .\nachricht.eml | ConvertTo-Json -Depth 6 | Set-Content .\analyse.json

Writes the complete analysis as JSON. -Depth 6 is required because Hops, DkimSignatures and Exchange are nested objects; the default value of 2 would output only their type names.

Parameters

-Header

The header as text. The parameter accepts a single string containing the entire header or multiple strings; pipeline input is collected and assembled into one header at the end, so Get-Content datei | Get-MailHeaderAnalysis works without -Raw. To analyze multiple headers separately, use -Path with multiple files.

Aliases: Text, Raw, InputObject

Parameter propertyValue
TypeString[]
Default valueNone
Wildcards supportedNo
Text parameter setValue
Position0
RequiredYes
Value from pipelineYes
Value from pipeline by property nameNo

-Path

Path to a file containing the header or a complete .eml message. Relative paths are resolved against the current directory. The file is read with [System.IO.File]::ReadAllText: a byte order mark is honored; without a BOM, UTF-8 is used. Each file produces a separate result object; missing files generate a non-terminating error.

Aliases: FullName, PSPath, LiteralPath

Parameter propertyValue
TypeString[]
Default valueNone
Wildcards supportedNo
Path parameter setValue
PositionNamed
RequiredYes
Value from pipelineNo
Value from pipeline by property nameYes

-FromClipboard

Reads the header from the clipboard with Get-Clipboard -Raw. The parameter is available only on Windows; on Linux and macOS, the cmdlet terminates with an error, as it does if the clipboard is empty.

Parameter propertyValue
TypeSwitchParameter
Default valueFalse
Wildcards supportedNo
Clipboard parameter setValue
PositionNamed
RequiredYes
Value from pipelineNo
Value from pipeline by property nameNo

-TrustedAuthServId

The authserv-id or authserv-ids that your inbound gateway writes in Authentication-Results, for example mx.example.org. The cmdlet compares exactly, case-insensitively and without subdomains. Authentication lines with one of these IDs receive AuthTrust = Trusted, and only they are then included in Spf, Dkim, Dmarc and Arc. The same applies to the receiver= of a Received-SPF line.

The result is only as reliable as the gateway: it must remove incoming Authentication-Results lines that claim its own authserv-id (RFC 8601, section 5). Whether it does so cannot be determined from a header. If two lines with a trusted ID conflict, the cmdlet reports AuthTrustedConflict. Microsoft 365 does not write an authserv-id in its authentication line; if Exchange Online receives your mail, omit the parameter.

For an entire session, you can set the value as a default, for example in your PowerShell profile:

$PSDefaultParameterValues['Get-MailHeaderAnalysis:TrustedAuthServId'] = 'mx.example.org'
Parameter propertyValue
TypeString[]
Default valueNone
Wildcards supportedNo
Parameter set (all)Value
PositionNamed
RequiredNo
Value from pipelineNo
Value from pipeline by property nameNo

Inputs

System.String: Header lines or the entire header, bound to -Header.

System.IO.FileInfo: File objects from Get-ChildItem, whose FullName is bound to -Path.

Outputs

MailHeaderAnalyzer.Analysis: one object for each analyzed header. The properties are described in the Output object section.

Notes

The analysis text (explanations in Findings, CompAuthReasonMeaning and the meaning fields) is in English so it can be pasted unchanged into international tickets. You can display the default view in full with Format-List *.

ConvertTo-MailHeaderReport

Creates a report from an analysis object as Markdown or text.

Syntax

ConvertTo-MailHeaderReport
    [-Analysis] <Object>
    [-Format <String>]
    [<CommonParameters>]

Description

The report includes the subject, sender, date, and Message-ID; authentication results with origin and alignment; all findings; the delivery chain; the Exchange classification; and spam filter values. Unicode control characters for text direction remain visible in the report as <U+...> so they cannot enter a ticket system through the report. The last line states the module version.

Examples

Example 1

Get-MailHeaderAnalysis -Path .\nachricht.eml | ConvertTo-MailHeaderReport | Set-Clipboard

Creates a Markdown report and places it in the clipboard.

# Email header analysis

- Subject: Service-Report für März
- From: Beispiel Newsletter <news@example.org>
- Date: 2026-08-03T09:14:27Z
- Message-ID: <20260803091427.4Wq2Wx5RbTz@mail.example.org>

## Authentication

- spf: pass
- dkim: pass
- dmarc: pass
- compauth: pass (reason=100: passed authentication)
- Results without authserv-id (Microsoft 365 style)
- DMARC alignment: SPF Strict, DKIM Strict

## Delivery chain (total: 42 s)

| # | From | By | Protocol | TLS | Time (UTC) | Delay |
|---|---|---|---|---|---|---|
| 1 | client.example.net (198.51.100.34) | mail.example.org | ESMTPSA | - | 2026-08-03T09:14:28Z | - |
| 2 | mail.example.org (203.0.113.25) | mx.eur02.prod.protection.outlook.com | Microsoft SMTP Server | TLS 1.3 | 2026-08-03T09:15:09Z | 41 s |
| 3 | AM0EUR02FT056.eop-EUR02.prod.protection.outlook.com | ZR0P278MB0570.CHEP278.PROD.OUTLOOK.COM | Microsoft SMTP Server | TLS 1.2 | 2026-08-03T09:15:10Z | 1 s |

Example 2

Get-MailHeaderAnalysis -FromClipboard | ConvertTo-MailHeaderReport -Format Text

Outputs the report as text without Markdown markup, for example for emails or console logs.

Example 3

Import-Module MailHeaderAnalyzer
Get-MailHeaderAnalysis -Path 'C:\Temp\bounce.eml' | ConvertTo-MailHeaderReport -Format Text

Use in the Exchange Management Shell. If $env:PSModulePath is restricted there by Group Policy, load the module with Import-Module and the full path to the .psd1 file.

Parameters

-Analysis

The analysis object from Get-MailHeaderAnalysis. The cmdlet rejects other object types with a binding error.

Parameter propertyValue
TypeMailHeaderAnalyzer.Analysis
Default valueNone
Wildcards supportedNo
Parameter set (all)Value
Position0
RequiredYes
Value from pipelineYes
Value from pipeline by property nameNo

-Format

The output format. Valid values:

  • Markdown: Headings, lists, and the delivery chain as a table. Default.
  • Text: Headings in uppercase, indented lines, and the delivery chain as a numbered list.
Parameter propertyValue
TypeString
Allowed valuesMarkdown, Text
Default valueMarkdown
Wildcards supportedNo
Parameter set (all)Value
PositionNamed
RequiredNo
Value from pipelineNo
Value from pipeline by property nameNo

Inputs

MailHeaderAnalyzer.Analysis: the result of Get-MailHeaderAnalysis.

Outputs

System.String: the report, one string per analysis object.

Output object

Get-MailHeaderAnalysis returns one object of type MailHeaderAnalyzer.Analysis for each input. The default view displays the summary from Example 1; all properties are available through Select-Object, Format-List * or ConvertTo-Json.

MailHeaderAnalyzer.Analysis

PropertyTypeContents
SourceStringFile path, Clipboard or Text
SubjectStringSubject, RFC 2047 decoded
From, ReplyTo, ReturnPathAddress objectName, Address, Domain, Display; $null if the field is absent
DateDateTime (UTC)Value of the Date field
MessageIdStringMessage-ID
MailFromDomainStringEnvelope sender domain from smtp.mailfrom of the SPF check, otherwise from Return-Path
Spf, Dkim, Dmarc, Arc, CompAuthStringResult according to the authoritative Authentication-Results line (pass, fail, none, softfail and others); $null if not checked. Arc is the recipient’s verdict on the ARC chain
CompAuthReason, CompAuthReasonMeaningStringReason code for Microsoft 365 composite authentication and its meaning
AuthTrustStringTrusted, Matched, Unmatched, Absent or None, see AuthTrust
AuthServIdStringauthserv-id of the authoritative authentication line
AuthenticationResultsObject[]All Authentication-Results lines with AuthServId, Methods, Trust, Raw
ReceivedSpfObjectThe Received-SPF line with Result and Properties
SpfAlignment, DkimAlignmentStringStrict, Relaxed, None or $null
HopsHop[]Delivery chain in chronological order; see Hop object
HopCount, TotalDuration, SlowestHopIndex, HasClockSkewInt, TimeSpan, Int, BoolChain metrics
DeliveredByStringby host of the most recent Received line, i.e., the delivering hop
DkimSignaturesObject[]Per signature: Domain, Selector, Algorithm, Canonicalization, SignedHeaders, BodyLength, Timestamp, Expires, ReceiverResult, Tags
ArcChainObject[]ARC instances with Instance, SealDomain, ChainValidation, Methods
ArcStructure, ArcStructureIssuesString, String[]Structure of the ARC chain: Consistent, Inconsistent or $null without ARC headers, plus the deviations found. Structure check only, not signature verification; see ARC chain
ExchangeObjectExchange Online hybrid classification; see Exchange object; $null without corresponding headers
SpamObjectSpam filter assessments; see Spam object; $null without corresponding headers
ListObjectListId, Unsubscribe, OneClick (RFC 8058); $null without list headers
FindingsFinding[]Anomalies with Severity, Code, Message, see Findings
FieldsObject[]All fields with Name, Value (unfolded), and Raw
HadBodyBoolWhether a message body followed the header

MailHeaderAnalyzer.Hop

Each entry in Hops corresponds to a Received line. The order is chronological, the reverse of the header order.

PropertyContents
IndexSequence number; 1 = submission
FromHost, Helo, ReverseDns, IPAddress, IsPrivateIPDetails about the submitting system from the from part; the IP comes from the square brackets in the comment, and the rDNS name from the preceding comment
ByHost, SoftwareReceiving system and its software (comment after by)
Protocol, ProtocolClasswith value and class according to RFC 3848: TlsAuthenticated (ESMTPSA), Tls (ESMTPS or TLS details present), Authenticated (ESMTPA), Plain, Http, Mapi, Local
TlsVersion, TlsCipherFrom notation used by Microsoft (version=TLS1_2, cipher=…), Postfix (using TLSv1.3 with cipher …) and Exim
Id, For, ViaOther Received components
DateTimestamp after the semicolon, UTC
DelayTimeSpan to the preceding hop; negative in case of clock skew
ProviderDetected vendor or gateway based on host names (Microsoft 365, Google Workspace, SEPPmail, HIN, Proofpoint, Mimecast, and others)
Attested$true only for the final hop: only this line was written by the receiving system itself; all lines below it were already in the message
RawThe original line

AuthTrust: origin of authentication results

Any sender can write an Authentication-Results line into a message themselves, as well as a matching Received line. Therefore, the header alone cannot establish who wrote an authentication line. According to RFC 8601, section 5, only the line whose authserv-id the receiving organization recognizes as its own is authoritative, and the inbound gateway must remove incoming lines with that ID. The cmdlet implements this rule with -TrustedAuthServId. Without this parameter, it compares the authserv-id only with the by hosts in the Received chain (same domain or subdomain, always at the dot boundary, never a substring); this is a plausibility check.

ValueMeaning
TrustedThe authserv-id is listed in -TrustedAuthServId. As soon as such a line exists, only lines at this level are included in Spf, Dkim, Dmarc and Arc. Reliable provided that the gateway removes foreign lines with this ID
MatchedThe authserv-id appears as a by host in the chain. Plausible, but not evidence: a forgery can include the matching Received line. Without -TrustedAuthServId, finding AuthPlausibleOnly points this out. Lines at this level count if no Trusted line is present
UnmatchedThe authserv-id is neither trusted nor found in the chain. Results are displayed but are treated as unsubstantiated claims; finding AuthUnverified points this out
AbsentThe line has no authserv-id. Microsoft 365 writes its authentication line in this form; it starts directly with spf=
NoneNo authentication line present

If the header contains authentication lines from several origins, finding AuthMixedOrigins reports this. If an authentication line is absent but a Received-SPF line is present, its result is adopted as Spf and identified with ReceivedSpfOnly.

With -TrustedAuthServId, two findings are added: AuthNotTrusted when no authentication line has a trusted ID, and AuthTrustedConflict when two such lines report different results for spf, dmarc, arc or compauth. The second case means that at least one line did not originate from the gateway and that the gateway did not remove it. In this case, the cmdlet uses the topmost line; the header cannot determine which of the two is genuine. DKIM is excluded from this comparison because multiple signatures may legitimately have different results.

ARC chain: structure and verdict

For ARC, the cmdlet provides two separate values. Arc is the arc= result from the authoritative authentication line, i.e., the verdict of the recipient that verified the chain’s signatures. ArcStructure is the module’s own check and concerns only the structure according to RFC 8617: consecutive instance numbers i=1 through i=n (maximum 50), exactly one ARC-Seal, one ARC-Message-Signature, and one ARC-Authentication-Results per instance, plus cv=none for instance 1 and cv=pass for all subsequent instances. The module does not verify signatures; Consistent therefore says nothing about whether the chain is genuine. Deviations appear in ArcStructureIssues and finding ArcStructureInconsistent.

The details in ARC-Authentication-Results are claims by the respective forwarder. Finding DkimBrokenAfterForward therefore classifies a DKIM failure as the result of forwarding only when the recipient itself reports arc=pass and an earlier instance recorded a DKIM pass for the same domain.

DMARC alignment

SpfAlignment compares the envelope sender domain with the From domain; DkimAlignment compares the d= domain of the verified signature with the From domain. Strict means identical domain, Relaxed means the same organizational domain, and None means no match. The organizational domain is determined heuristically: the last two labels, or the last three for known multipart suffixes such as co.uk or com.au. A complete Public Suffix List is not included.

MailHeaderAnalyzer.ExchangeClassification

If the header contains X-MS-Exchange-Organization-*, X-OriginatorOrg or X-MS-Exchange-CrossTenant-* fields, the cmdlet populates the Exchange property. The meanings follow the Exchange Team article “Demystifying hybrid mail flow”; background is provided in Exchange hybrid headers: internal or external?.

PropertyContents
Directionality, DirectionalityMeaningOriginating or Incoming with explanation
AuthAs, AuthAsMeaningInternal or Anonymous with the implications for EOP filtering
AuthSourceServer that assigned the classification
AuthMechanism, AuthMechanismMeaningMechanism code. Only value 10 (Externally Secured) is publicly documented; for all other codes, the module explicitly states that Microsoft does not document them
OriginatorOrgDefault domain of the sending tenant, the non-forgeable tenant identifier when receiving from Microsoft 365
CrossTenantAuthAs, CrossTenantAuthSource, CrossTenantIdClassification and tenant ID at the tenant boundary
CrossTenantFromEntity, CrossTenantFromMeaningInternet, Hosted or HybridOnPrem with explanation
WrongTenantAttributionValue of X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp when the message was assigned to another tenant
OrganizationHeadersPreserved, CrossPremisesHeadersFilteredMarkers for received organizational headers and organizational headers removed by the send connector, respectively

MailHeaderAnalyzer.SpamAssessment

The Spam property combines assessments from known filters. Values originate from external systems and are decoded but not evaluated.

PropertySourceContents
Scl, SclMeaningX-Forefront-Antispam-Report, X-Microsoft-AntispamSpam Confidence Level with meaning (-1 trusted, 0/1 not spam, 5/6 suspected spam, 9 very likely spam)
BclX-Microsoft-AntispamBulk Complaint Level 0 through 9
Category, CategoryMeaningCAT:Classification such as SPM, PHSH, HPHSH, MALW, SPOOF, DIMP, UIMP, BULK
SpamFilterVerdict, SpamFilterMeaningSFV:Filter result such as NSPM, SPM, SKA (allowlist), SKI (intraorganizational)
IPVerdict, IPVerdictMeaningIPV:CAL (IP on connection allowlist) or NLI (no reputation)
Direction, DirectionMeaningDIR:INB, OUT, INT
ConnectingIP, CountryCIP:, CTRY:Submitting IP and country of origin
ForefrontX-Forefront-Antispam-ReportAll key-value pairs in the field
SpamAssassinScore, SpamAssassinTestsX-Spam-StatusScore and triggered tests
RspamdSymbolsX-Spamd-Result, X-Spam-ReportSymbols with scores

The complete list of compauth reason codes is available in Microsoft 365 compauth: Reason Codes.

Findings

The cmdlet returns anomalies as objects in Findings, each with Severity (Info, Warning, Fail), a stable Code for filters and scripts, and an explanation in Message.

CodeSeverityMeaning
DuplicateFieldWarningA field limited by RFC 5322 to one instance (From, Subject, Date, Message-ID and others) occurs more than once. Mail clients and filters may choose different instances; a known forgery pattern
BidiControlsWarningUnicode control character for text direction in a field. It reverses the reading direction, so fdp.exe then appears as exe.pdf. The module displays it as <U+202E>
HopOverflowWarningMore than 200 Received lines; the excess lines were not analyzed
AuthPlausibleOnlyInfoThe authserv-id occurs in the delivery chain, but -TrustedAuthServId was not specified: plausible, not evidence
AuthNotTrustedWarning-TrustedAuthServId was specified, but no authentication line has one of these IDs
AuthTrustedConflictWarningTwo authentication lines with a trusted ID report different results for the same method; the gateway apparently does not remove foreign lines
AuthUnverifiedWarningThe authentication results have an authserv-id that is neither trusted nor present in the delivery chain
AuthMixedOriginsWarningAuthentication lines from multiple origins are present
ReceivedSpfForeignWarningThe receiver= of the Received-SPF line does not occur in the chain
ReceivedSpfOnlyInfoThe SPF result originates only from Received-SPF, not from an authentication line
NoAuthResultsInfoNo authentication results in the header
DmarcFailFailDMARC failed according to the receiving server
SpfNotPassWarningSPF result fail, softfail, permerror or temperror
DkimNotPassWarningDKIM result fail, permerror or temperror, without the recipient confirming the ARC chain
DkimBrokenAfterForwardInfoDKIM failed at the recipient, but the recipient reports arc=pass, and an earlier ARC instance recorded a DKIM pass for the same domain: typical of forwarding and mailing lists
DkimWeakHashWarningSignature with rsa-sha1 (RFC 8301 classifies SHA-1 as obsolete)
DkimBodyLengthWarningThe l= tag limits the signed length of the message body; appended content is not covered
DkimExpiredWarningThe x= timestamp is in the past
DkimFromUnsignedWarningThe From field is not included in h=, although RFC 6376 requires it
ArcStructureInconsistentWarningThe ARC headers do not form a formally complete chain (gaps, missing or duplicate headers, incorrect cv= sequence); structure check only
ClockSkewInfoA hop has an earlier timestamp than its predecessor; delays are only approximate
ReplyToMismatchInfoThe Reply-To domain differs from the From domain; common for newsletters, but a phishing pattern
SpfNotAlignedInfoThe envelope sender domain and From domain belong to different organizations; SPF then does not contribute to DMARC
ExchangeWrongTenantWarningThe message was assigned to a different tenant; a classic cause is an inbound connector from another tenant with the same certificate or IP addresses
ExchangeHeadersFilteredWarningThe send connector removed the cross-premises headers (KB3212872)
ExchangeExternallySecuredWarningAuthMechanism 10: received through a receive connector with “Externally Secured”; EOP filtering skipped
SpamCategoryWarningMicrosoft assigned a category other than NONE
SpamConfidenceWarningSCL 5 or higher

How it works and limitations

The module reads what is in the header and derives what can be established without external queries. This entails several limitations:

  • No cryptographic verification. DKIM and ARC signatures are not recalculated, and DNS records are not queried. Spf, Dkim, Dmarc and Arc are always the receiving server’s verdict; ArcStructure checks only the chain’s structure.
  • Origin can be evidenced only with knowledge of the gateway. Without -TrustedAuthServId, AuthTrust is a plausibility check. With the parameter, the statement depends on the gateway removing foreign authentication lines using its authserv-id; the module cannot verify that.
  • Only the final Received line is evidenced. The sender supplied all lines below it and can construct them arbitrarily. Attested marks this distinction; delays at earlier hops are based on the information in those lines.
  • Organizational domains are heuristic. For relaxed alignment, the module uses a short list of multipart suffixes, not a complete Public Suffix List.
  • AuthMechanism is only partially documented. Other than value 10, Microsoft has not published the codes; the module does not invent meanings.
  • Character sets. RFC 2047 values are decoded using encodings that .NET recognizes on the respective system. Unknown character sets remain unchanged.

Privacy

A complete header contains internal host names, IP addresses, senders, recipients, and subjects. The article Analyze email headers without uploading the email explains why this information does not belong in an online tool. The same promise applies to the module as to the browser version: no network access. The module’s test suite includes a test that fails as soon as a network or DNS cmdlet appears in the source code.

Source code and versions

The source code is available under the MIT license on GitHub. The analysis logic is a port of the library also used by the Header Analyzer on this website; both share the test cases. Continuous integration tests every change with PSScriptAnalyzer and Pester on Windows PowerShell 5.1, PowerShell 7 on Windows, Ubuntu, and macOS. Releases to the PowerShell Gallery are published automatically from versioned tags; changes per version are listed in the changelog.

Version 0.2.0 of September 26, 2026 tightened the origin model following a review note from @saltyslugga: parameter -TrustedAuthServId, level Trusted, and Matched only as plausibility. The ArcValid property was removed and replaced by ArcStructure and ArcStructureIssues; scripts that evaluate ArcValid must be updated.

I accept bugs and feature requests as issues on GitHub. Please anonymize test headers before submitting bug reports; the included test cases use only example domains under RFC 2606 and addresses under RFC 5737.

Change log

  • October 25, 2026: Replaced source RFC 7489 with RFC 9989, which supersedes the DMARC specification as of May 2026.

Sources

  1. MailHeaderAnalyzer in the PowerShell Gallery

    package page with installation command and version history.

    https://www.powershellgallery.com/packages/MailHeaderAnalyzer
  2. pfstr/MailHeaderAnalyzer on GitHub

    source code, test suite, changelog, and issues.

    https://github.com/pfstr/MailHeaderAnalyzer
  3. Microsoft Learn: Get-MessageTraceV2

    template for the structure of this reference (syntax, description, examples, parameter properties).

    https://learn.microsoft.com/en-us/powershell/module/exchangepowershell/get-messagetracev2?view=exchange-ps
  4. RFC 8601: Message Header Field for Indicating Message Authentication Status

    structure of Authentication-Results and the rule that only the receiving organization’s line is authoritative (section 5).

    https://www.rfc-editor.org/rfc/rfc8601
  5. RFC 5321: Simple Mail Transfer Protocol

    structure of Received lines and the recommendation for an upper limit as loop protection (section 6.3).

    https://www.rfc-editor.org/rfc/rfc5321
  6. RFC 3848: ESMTP and LMTP Transmission Types Registration

    the with values ESMTPS, ESMTPA and ESMTPSA.

    https://www.rfc-editor.org/rfc/rfc3848
  7. RFC 6376: DomainKeys Identified Mail (DKIM) Signatures

    tags of DKIM-Signature, requirement to sign From.

    https://www.rfc-editor.org/rfc/rfc6376
  8. RFC 8301: Cryptographic Algorithm and Key Usage Update to DKIM

    classification of rsa-sha1 as obsolete.

    https://www.rfc-editor.org/rfc/rfc8301
  9. RFC 9989: Domain-Based Message Authentication, Reporting, and Conformance (DMARC)

    strict and relaxed alignment; supersedes RFC 7489 as of May 2026.

    https://www.rfc-editor.org/rfc/rfc9989
  10. RFC 8617: The Authenticated Received Chain (ARC) Protocol

    structure of the chain consisting of ARC-Seal, ARC-Message-Signature and ARC-Authentication-Results, instance numbers, and cv= values.

    https://www.rfc-editor.org/rfc/rfc8617
  11. Microsoft Learn: Anti-spam message headers in Microsoft 365

    meaning of SCL, BCL, CAT, SFV, IPV, and compauth reason codes.

    https://learn.microsoft.com/en-us/defender-office-365/message-headers-eop-mdo
  12. Exchange Team Blog: Demystifying hybrid mail flow
  13. Header Analyzer on rafaelpfister.ch

    the browser version with the same analysis logic.

    https://rafaelpfister.ch/tools/header-analyzer

Comments

Comments are loaded from GitHub / Giscus.