Skip to content
← Blog

How to read an email's headers

By Thomas · virtual CISO · August 22, 2026

A message displays a sender, a subject, a date. All of it is text, and all of it can be forged. What resists forgery lives elsewhere: in the dozens of technical fields that mail clients hide by default, and that the receiving server wrote itself when the message arrived. Those fields — the headers — answer the two questions anyone asks in front of a suspicious email, or in front of a legitimate one that landed in the spam folder: where did it really come from, and what broke along the way.

This guide describes the anatomy of those headers, the order in which reading them pays off, and the boundary — often drawn in the wrong place — between what they demonstrate and what they merely assert.

Where the headers live

Every mail client buries the information somewhere different, and the vocabulary varies more than the thing itself.

  • Gmail (web): the "⋮" menu on an open message, then "Show original". A page opens with the headers at the top and the raw message below.
  • Outlook (desktop): "File" → "Properties", then the "Internet headers" box at the bottom of the window. The box is small; its contents can be copied in full.
  • Outlook.com / Microsoft 365 (web): the "⋯" menu → "View" → "View message source".
  • Apple Mail: "View" → "Message" → "All Headers".
  • Thunderbird: "View" → "Message Source", or Ctrl+U.

One point deserves a mention before any reading begins: headers do not survive a forward. Forwarding a suspicious message to a colleague produces a new message, with new headers — those of the person forwarding. The original becomes an attachment or quoted text, and its authentication is no longer readable. That is why security teams ask for a message to be sent "as an attachment" (.eml) rather than simply forwarded.

Reading order: three blocks, not forty fields

An ordinary message carries between thirty and a hundred headers. Almost none of them teach anything: internal identifiers, proprietary antispam fingerprints, queue markers. Three blocks hold the value.

The first is the identities. From: is the displayed address, the one the recipient reads and the only one DMARC protects. Return-Path: carries the envelope address, the one bounce notifications go to — frequently different, and that difference is normal as soon as a third-party sender is involved. Reply-To:, where present, says where a reply will go: a gap between From: and Reply-To: is a staple of business email compromise.

The second is the verdicts: Authentication-Results, and the signature headers that accompany it.

The third is the route: the stack of Received: fields.

Authentication-Results: the verdict, and its trap

This is the decisive header, standardised by RFC 8601. The server that accepts the message records in it the outcome of the checks it has just run:

Authentication-Results: mx.example.com;
       dkim=pass header.d=example.com header.s=s2026;
       spf=pass smtp.mailfrom=bounces.router.example;
       dmarc=pass header.from=example.com

Three methods, three results, and properties attached to each. header.d= names the domain that signed with DKIM; header.s= gives the selector that points to the public key; smtp.mailfrom= gives the envelope domain SPF validated.

And here is the trap most readings miss: this header can be written freely. Nothing prevents an attacker from inserting, into the message they send, an Authentication-Results line proclaiming dkim=pass for the bank they are impersonating. Only the header added by the recipient's own receiving server carries weight — the rest is text supplied by the sender.

Two markers settle the question. First the opening segment, the authserv-id: it names the server claiming to have run the check, and it should match the real receiving domain. Second the order: each relay prepends its own header, so the topmost one in the message is the most recent, the one from the final boundary. A message carrying three Authentication-Results headers crossed three boundaries — or is transporting two that were written somewhere else entirely.

Alignment, the step the verdict does not show

An isolated spf=pass says almost nothing. SPF validates the envelope domain, not the one the recipient sees. When a newsletter goes out through a third-party sender, the envelope belongs to that sender and SPF passes legitimately — without covering the domain shown in From:. DMARC demands more: that the method which passes be aligned with the From: domain.

Reading it is a matter of comparing two strings. Does the header.d= domain cover the From: domain? Does the smtp.mailfrom= domain cover it? Only one of the two needs to answer yes for DMARC to be satisfied. In the example above, SPF passes without being aligned, DKIM passes and is aligned, so the message is compliant. That case — a valid SPF result that still lets DMARC fail — is the single most common misunderstanding of the protocol, and it takes three seconds to spot in the headers.

DKIM-Signature: what the signature covers

The signature header travels in the original message, independently of any verdict:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=example.com; s=s2026; h=from:to:subject:date;
        bh=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=; b=Xk9dQ2mF...

d= and s= identify the public key to query in DNS. h= lists the fields the signature covers — and that list rewards attention, because anything absent from it can be altered without invalidating the signature. bh= is the body hash, b= the signature itself.

A practical consequence follows: the presence of a DKIM-Signature proves nothing on its own. Verifying a signature means recomputing those hashes, which requires the complete message body and a DNS lookup. That is the receiving server's job, and the procedure for checking a DKIM signature by hand covers it. Reading headers only establishes that the signature exists, which domain it claims, and what the server concluded from it.

Received:: the route, read backwards

Every server that takes charge of the message prepends a Received: field to the stack. The consequence is counter-intuitive: the first Received: in the message is the last hop chronologically, and the route reads from the bottom up.

Received: from relay.example.com (relay.example.com [198.51.100.24])
        by mx.recipient.example with ESMTPS id 4Wx1Yz;
        Wed, 5 Aug 2026 09:14:50 -0700 (PDT)

Each line gives the announced host (from), the receiving host (by), the protocol (with), usually the IP address in brackets, and a timestamp. Three readings follow.

The first is the true origin: the IP address of the oldest hop, belonging to the server that actually sent the message. It survives any forgery of From:, and it is the address an abuse complaint to a hosting provider must cite.

The second is delay. Comparing the timestamps of two consecutive hops gives the time spent at each stage. A few seconds are normal. Several minutes point to a queue, to greylisting, or to intermediate filtering — and often explain why a message "arrived late" without anyone having blocked it. This is one of the first threads to pull when legitimate messages end up in the spam folder.

The third is the number of boundaries crossed. A direct message makes two or three hops. Ten of them betray a chain of redirections, and therefore a context where SPF stands a good chance of being broken.

ARC, mailing lists and forwarding

When an intermediary modifies a message — a mailing list appending a footer, a system rewriting the subject — the original DKIM signature stops verifying and forwarding breaks the SPF authorisation. The legitimate message becomes indistinguishable from a spoof.

ARC answers that problem by having the intermediary testify. Three headers then appear, numbered by instance: ARC-Authentication-Results preserves a snapshot of the verdicts observed before modification, ARC-Message-Signature signs the message as it is retransmitted, and ARC-Seal seals the whole set. The cv= tag on the highest-numbered seal gives the state of the chain: pass for an intact chain, none for a first link, fail for a broken one. How the ARC chain works in detail deserves an article of its own; for header reading, its mere presence is enough to explain an authentication result that collapses without any fraud involved.

The List-Id and List-Unsubscribe fields tell the same story more simply: the message came from a list, and lists rewrite senders freely.

What headers will never say

A message is one case. It proves what happened to that message, and nothing else. Headers do not say how many other messages were sent under the same domain that day, how many failed, or from which IP addresses. A domain can present one perfectly authenticated message while an entire stream of spoofing passes elsewhere.

That is the structural limit of the exercise, and the reason aggregate reports exist. Where a header shows one case, reading DMARC aggregate reports shows every sender of a domain, day after day, and makes it possible to spot spoofing in the reports that nobody would ever have flagged. The two readings complement each other: one answers "why this message", the other "what is happening on this domain".

To skip the manual dissection, a free email header analyzer produces the same findings in one pass: verdicts per method, recomputed alignment, ARC chain and reconstructed route. And for the domain-wide view, the free DMARC analyzer starts from the other end — the published configuration rather than a received message.

Frequently asked questions

Can a header be forged outright? Anything the sender writes can be: From:, Reply-To:, Date:, and even fake Received: or fake Authentication-Results lines inserted before sending. What cannot be forged are the fields added after sending by the servers along the way — they stack on top. Hence the rule: trust decreases going down the stack.

Why does Return-Path: differ from From:? Because they are two distinct identities in the protocol. The envelope serves routing and bounces; the displayed address serves the human. A third-party sender almost always uses its own envelope, and the gap is not abnormal in itself — it becomes a problem only when no aligned DKIM signature compensates for it.

Do headers contain personal data? They do: email addresses, IP addresses, sometimes internal server names. A block of headers pasted into a public ticket or sent to a third-party service exposes all of it. The precaution is worth taking before sharing a message in the clear.

In short

Headers are the only part of an email that keeps a record of its own past. Three blocks are enough: the identities, to know who the message claims to be; Authentication-Results, to know what the receiving server concluded — always checking which server is speaking; and the Received: stack, to reconstruct where it came from and how long it took. The rest is technical noise. And the day the question shifts from "this message" to "this domain", aggregate reports are where the answer lives.

Related guides

About the author

ThomasThomas is the virtual CISO of DMARC.com: a copilot specialized in email authentication that walks organizations from p=none to p=reject without breaking their mail. His guides draw on real data from the DMARC Observatory and the RUA reports the platform analyzes.