Tracing a delivery through Received headers
Received headers record every hop a message took, newest first. Read them bottom-up to find delays, rewrites and the server that touched your mail.

On this page(10 sections)
Every mail server that handles a message is expected to add a Received header to the top of it. Read in the right order, those headers are a hop-by-hop travel log: where the message came from, which servers touched it, how long each step took, and whether the connection was encrypted.
What a Received header contains
RFC 5321 requires SMTP servers to add a trace line when they accept a message. A typical one looks like this:
Received: from mail-out.example.com (mail-out.example.com [192.0.2.25])
by mx1.example.net with ESMTPS id 4kH2Lq0QzVz9s
for <[email protected]>; Wed, 30 Sep 2026 14:02:13 +0000 (UTC)
The common clauses:
- from: how the sending client identified itself (its EHLO name), often followed by the receiving server's own view of it: a reverse DNS name and the connecting IP in brackets.
- by: the server that added this header.
- with: the protocol, such as
SMTP,ESMTP,ESMTPS(with TLS) orESMTPSA(with TLS and authentication). These protocol names are registered in RFC 3848. - id: the receiving server's internal queue ID, useful when asking its operator to search logs.
- for: the recipient, sometimes omitted for privacy.
- date: when this server received the message, after the semicolon.
Formats vary between mail software, and many servers add extra details such as TLS version and cipher in comments.
Read from the bottom up
Each server adds its header at the top, so the newest hop is first and the oldest is last. To follow the message's journey, start at the bottom Received header (closest to the origin) and read upward.
Here is a simplified trace with four hops, shown top to bottom as they appear in a message:
Received: by 2002:a05:6000:1::1 with SMTP id x9; Wed, 30 Sep 2026 07:02:15 -0700 (PDT)
Received: from mx1.example.net (mx1.example.net [198.51.100.7])
by inbound-filter.example.net with ESMTPS id 8f2k; Wed, 30 Sep 2026 14:02:14 +0000
Received: from mail-out.example.com (mail-out.example.com [192.0.2.25])
by mx1.example.net with ESMTPS id 4kH2Lq0QzVz9s; Wed, 30 Sep 2026 14:02:13 +0000
Received: from app-worker-3.internal (unknown [10.0.4.18])
by mail-out.example.com with ESMTPSA id 77ab; Wed, 30 Sep 2026 14:02:11 +0000
Reading upward:
- An application worker on a private network submitted the message to the sender's outbound server, authenticated over TLS (
ESMTPSA). - The outbound server delivered to the recipient's MX over TLS two seconds later.
- The MX passed it to an internal filtering system one second later.
- The final internal hop delivered it to the mailbox store.
Finding delays
Compare timestamps between consecutive hops, converting time zones as you go. The first header in the example uses -0700; 07:02:15 PDT equals 14:02:15 UTC, so the final hop took one second.
| Hop | Received at (UTC) | Time since previous |
|---|---|---|
| Outbound server | 14:02:11 | (origin) |
| Recipient MX | 14:02:13 | 2 s |
| Internal filter | 14:02:14 | 1 s |
| Mailbox store | 14:02:15 | 1 s |
When a customer says a sign-in code arrived ten minutes late, a gap like this pinpoints the culprit:
- A gap at the first external hop usually means your outbound queue was backed up or the receiver deferred you and you retried later.
- A gap inside the recipient's infrastructure points to their filtering or routing, often a security gateway scanning links or attachments.
- Large gaps with retries often coincide with greylisting.
Be wary of clock skew. Each server stamps its own clock, and a badly synchronized server can make a message appear to travel backward in time. Treat small negative gaps as skew, not as a mystery.
Checking TLS hop by hop
The with clause shows whether each hop used TLS. ESMTPS and ESMTPSA indicate TLS; plain ESMTP or SMTP between organizations means that hop was unencrypted. Many servers also add a comment with the TLS version and cipher, such as (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384).
If you care about transport encryption, for example because you have deployed MTA-STS, Received headers on messages arriving at your domain are a quick way to spot senders still delivering without TLS.
What you can trust
Received headers are only as trustworthy as the server that wrote them. The headers your own servers add are reliable. Headers added before the message reached your infrastructure were written by others and can be forged: a spammer can prepend fake Received lines to make a message look like it passed through respectable servers.
The trust boundary is the first Received header written by your own receiving server, the one whose by clause names your MX. The from clause in that header records the IP that actually connected to you, which cannot be faked at the TCP level. Everything below it is the sender's claim.
Other trace headers worth knowing
Received headers are not the only trace information in a message:
- Return-Path is added by the final delivering server and records the envelope sender, which is the domain SPF was checked against.
- Authentication-Results records the SPF, DKIM and DMARC verdicts a receiver reached. Like Received, it is trustworthy only when added by your own server; receivers are expected to remove copies that claim to come from them.
- ARC headers, when present, record authentication results observed by intermediaries such as mailing lists and forwarders.
- X- headers added by filters and gateways often contain spam scores and policy decisions. They are not standardized, but they frequently explain why a message was quarantined.
Reading these together with the Received chain gives you the full story: the route, the timing and the authentication verdicts along the way.
Practical uses
- Investigating delays: find the hop with the largest gap.
- Identifying the true origin: the connecting IP in your own MX's Received header is the source to check against SPF, blocklists and abuse reports.
- Confirming the path: verify that mail passed through the gateways and filters you expect, in the expected order.
- Supporting other admins: the
idvalue lets another operator find the message in their logs quickly. - Spotting forwarding: hops through an unexpected domain usually mean the recipient forwards mail, which explains SPF failures.
A quick extraction script
import email, sys
from email.utils import parsedate_to_datetime
msg = email.message_from_binary_file(open(sys.argv[1], "rb"))
hops = msg.get_all("Received", [])[::-1] # oldest first
prev = None
for h in hops:
flat = " ".join(h.split())
when = parsedate_to_datetime(flat.rsplit(";", 1)[-1].strip())
gap = f"+{(when - prev).total_seconds():.0f}s" if prev else "origin"
print(f"{when.isoformat()} {gap:>8} {flat[:110]}")
prev = when
Checklist
- Read Received headers from the bottom (oldest) to the top (newest).
- Normalize time zones before comparing timestamps.
- Look for the largest gap to locate delays.
- Check the
withclause on each hop for TLS. - Trust headers only from your own MX upward; treat earlier ones as claims.
- Note queue IDs when asking another administrator for help.
Key takeaways
- Received headers are a hop-by-hop log of a message's path, newest first.
- Timestamps between hops reveal where delays occur, with time zones normalized and skew in mind.
- The
withclause shows whether each hop used TLS. - Only headers added by your own servers are trustworthy; earlier ones can be forged.
Start with Koltrix
Your domain, one inbox, and an API that sends.
A team inbox where AI sorts and drafts (nothing is sent without your click), plus the transactional API and SMTP relay your product sends with. 7 days free, no card.


