A mail client shows you a sender, a date and a subject. All three are lines the sending software wrote, and nothing about the format prevents any of them being false. The header block also contains a record of what happened to the message on the way, written by machines rather than by the sender, and that is the part worth reading.
The From line proves nothing
From: is a header field like Subject:. Whatever composes the message writes it, and it can say anything.
It is also two things at once — a display name and an address — "Accounts Payable" <ap@supplier.example>. Most clients show the display name and hide the address behind it, on a phone always, which is why the oldest trick still works: put the real-looking address in the display name and something else in the actual one.
Reply-To: is a separate field again, and it is what your reply goes to. A message can display one address, be sent from a second and quietly route replies to a third, with nothing malformed about it.
And there is a fourth address that never appears in the client at all: the envelope sender, the one given during the SMTP conversation. It is usually recorded as Return-Path: at the top of the header block by the delivering server. Keep that one in mind — most of the confusion about SPF comes from not knowing it exists.
The Received chain, read upwards
Every server that handles a message adds a Received: line, and it adds it to the top of the block rather than the bottom. So the chain is in reverse: the first line you see is the last thing that happened, and the earliest hop is at the bottom.
Received: from mx.example-mail.net by imap.example-mail.net;
Tue, 4 Aug 2026 09:14:22 +0000
Received: from smtp-out-3.mailer-7x.co (smtp-out-3.mailer-7x.co [203.0.113.45])
by mx.example-mail.net with ESMTPS id 4Wq2Kd1f9c
for <you@example-mail.net>; Tue, 4 Aug 2026 09:14:21 +0000
Received: from workstation (unknown [198.51.100.9])
by smtp-out-3.mailer-7x.co; Tue, 4 Aug 2026 09:14:19 +0000Read from the bottom: something calling itself workstation, at 198.51.100.9, handed the message to smtp-out-3.mailer-7x.co. That machine passed it to mx.example-mail.net, which is the recipient's mail exchanger, from the address 203.0.113.45. Internal delivery finished the job a second later.
Two conventions inside a single line are worth knowing. from smtp-out-3.mailer-7x.co (smtp-out-3.mailer-7x.co [203.0.113.45]) is three separate facts: the name the connecting machine announced, the name its IP address resolves back to, and the address itself. The receiving server checked the second and third; the first is just a claim. When the announced name and the reverse lookup disagree, that is visible here and nowhere else. unknown in that position means the reverse lookup found nothing.
The timestamps run backwards as you read down, and the gaps are informative. Nineteen seconds between two lines is normal. Four hours means the message sat in a queue somewhere, which is the usual answer to "why did this arrive late" and takes about ten seconds to find.
The part of the chain that can be invented
This is the limit that matters, and most explanations of email headers leave it out.
A Received: line is added by whatever server handled the message. A server controlled by the sender can therefore add whatever lines it likes before sending, and they will sit at the bottom of the chain looking exactly like the real ones. Forging a plausible origin is a matter of writing three lines of text.
What cannot be forged is the line your own provider added, because your provider wrote it after observing the connection itself. So the chain has a boundary in it: everything from the first server you actually trust upwards is evidence, and everything below that point is the sender's account of itself.
In practice this means the useful line is the one where your provider recorded who connected to it — the IP address in that line is real, whatever the ones underneath it say.
SPF, DKIM and DMARC
Three separate mechanisms, and the reason they are confusing is that they each check a different thing about a different domain.
SPF is a list of permitted senders. A domain publishes a DNS record naming the IP addresses and hosts allowed to send mail for it, and the receiving server compares the address that connected against that list. What it checks the list against is the envelope sender — the Return-Path domain — not the address in the From line.
DKIM is a signature. The sending system signs the body and a named set of headers with a private key, and attaches the signature as DKIM-Signature:, which states the signing domain in d= and which key in s=. The receiver fetches the matching public key from that domain's DNS and recomputes the signature. A pass means the signed parts arrived unaltered and were signed by someone holding that domain's key. It says nothing about the headers the signature did not cover.
DMARC ties the two to the thing a person actually reads. It requires that SPF or DKIM passed and that the domain it passed for matches the domain in the visible From. That match is called alignment, and it is the entire point of DMARC. The domain also publishes what to do on failure — p=none means report only, p=quarantine means treat as suspect, p=reject means refuse it outright.
Reading Authentication-Results
Your provider runs all three at delivery and writes the outcome into one header. It is the fastest thing in the block to read once you know the shape.
Authentication-Results: mx.example-mail.net;
spf=pass (sender IP is 203.0.113.45) smtp.mailfrom=bounce.mailer-7x.co;
dkim=pass header.d=mailer-7x.co header.s=k1;
dmarc=fail (p=quarantine dis=none) header.from=yourbank.exampleTwo passes and a fail, and the fail is the one that decides it. SPF passed for bounce.mailer-7x.co. DKIM passed for mailer-7x.co. The message claims to be from yourbank.example. Nothing authenticated that domain at any point, so DMARC fails on alignment.
This is what a competent forgery looks like. The attacker registered a domain, published a correct SPF record for it, set up DKIM signing properly, and sent from their own infrastructure. Every mechanism worked exactly as designed. All three results are true. The message is still a fake, and the only line that says so is the third one.
The results themselves are worth reading precisely:
- pass — the check ran and succeeded, for the domain named beside it. Always read the domain, never the word alone.
- fail — the check ran and the answer was no. For SPF this means the domain publishes a list and the sending address is not on it, with
-allat the end saying to treat that as final. - softfail — the domain publishes a list ending
~all, which says "not on the list, but do not reject on my account". Extremely common and largely uninformative. - none — nothing was published to check against. No SPF record, no DKIM signature. It is an absence, not a failure, and a great deal of legitimate mail still looks like this.
- neutral, temperror, permerror — the domain explicitly declined to assert anything, a DNS lookup failed at the time, or the record is malformed. The last one usually means somebody exceeded the ten-lookup limit in their SPF record, which is a configuration bug rather than an attack.
Where forwarding breaks all of this
A mailing list or a forwarding address relays the message from its own servers, so the connecting IP is no longer the sender's and SPF fails on the way through. Lists also tend to add a footer or rewrite the subject, which changes bytes the DKIM signature covered, so DKIM fails too.
The result is genuine mail with two failures in it, which is why a bare failure is not proof of anything and why every serious answer here is about alignment rather than about any single result.
What the header does not contain
It is worth knowing the limits before hunting for something that was never written down.
The sender's own IP address, usually. Mail composed in a webmail interface is submitted by the provider's servers, and the major providers stopped recording the composer's address years ago. The bottom of the chain is their infrastructure, not a person's home connection.
A location. An IP address in a Received line belongs to whoever operates it, and a geolocation lookup on a data centre address tells you where the data centre is.
Whether the content is true. Authentication answers one question: whether the domain authorised the message. A genuinely-sent invoice from a compromised supplier account passes everything.
Reading one yourself
Every client can show you the raw headers — Show original in Gmail, View source or Properties elsewhere — and the header analyser here lays the Received chain out in order and explains the results line beside it.
It reads what the receiving server recorded rather than re-running the checks, and that distinction is deliberate: verifying SPF or DKIM independently means querying DNS for what the sender's domain publishes today, which a browser cannot do and which would in any case answer a different question — what the record says now, rather than what it said when the message arrived.
The whole block is inside the .eml, so opening the file is enough. It does not need to go anywhere, and there are reasons not to send it — a header block is a list of everyone who was on the thread.