Skip to content
emailmarketing.net

Reading Received Headers: Hops, Delays, and What They Prove

How to read the Received trace fields of a delivered message: the field syntax, bottom-to-top order, which hops you can trust, computing per-hop delay across time zones, and how Received relates to Authentication-Results.

Operationalesp-operatorsender

Every SMTP server that accepts a message adds a Received: line to the top of it. Read together, those lines record the route the message took and when each host received it. They are the first thing to check when a message arrived late or by an unexpected path. Some of the lines are reliable evidence and some are only claims, and this article explains how to tell which is which.

The rules come from RFC 5321 §4.4 (trace information) and RFC 5322 §3.6.7 (trace fields). The relationship with Authentication-Results comes from RFC 8601 (see the Authentication-Results header).

Where Received lines come from

RFC 5321 §4.4: "When an SMTP server receives a message for delivery or further processing, it MUST insert trace ("time stamp" or "Received") information at the beginning of the message content." Three consequences follow directly from the same section:

  • Each line is written by the receiving host, not the sender. A Received: line records one host accepting the message.
  • Lines are only added at the top. "SMTP servers MUST prepend Received lines to messages; they MUST NOT change the order of existing lines or insert Received lines in any other location."
  • Existing lines are not edited or removed. "An Internet mail program MUST NOT change or delete a Received: line that was previously added to the message header section."

These are requirements on compliant software, not guarantees about every message. RFC 3848 §3 notes that "some people in a misguided attempt to hide information about their internal servers will strip Received headers of useful information", so a chain can have gaps.

When the message leaves SMTP at final delivery, the delivering server also adds Return-Path:, which holds the envelope MAIL FROM address. RFC 5321 §4.4 says the delivered message will "begin with a return path line, followed by one or more time stamp lines."

RFC 5322 §3.6.7 treats these fields as "strictly informational, and any formal interpretation of them is outside of the scope of this document." There's nothing to validate: a Received: line is only as trustworthy as the host that wrote it.

Field syntax

RFC 5321 §4.4 defines the line (abridged; FWS/CFWS are folding whitespace and comments from RFC 5322):

Time-stamp-line = "Received:" FWS Stamp <CRLF>
Stamp           = From-domain By-domain Opt-info [CFWS] ";" FWS date-time
Opt-info        = [Via] [With] [ID] [For] [Additional-Registered-Clauses]

A line can be folded over several physical lines, so unfold it before reading. The clauses, in order:

Clause What it records Who supplies the value
from The host that handed the message over. RFC 5321: it "SHOULD contain both (1) the name of the source host as presented in the EHLO command and (2) an address literal containing the IP address of the source, determined from the TCP connection." The parenthesized part (TCP-info) is "Information derived by server from TCP connection not client EHLO." The name before the parentheses is what the client said in EHLO. The address inside the parentheses is what the receiving server observed on the connection.
by The host that received the message and wrote this line. The receiving server.
via The link type (TCP). RFC 5321: "'Via' is primarily of value with non-Internet transports." The receiving server.
with The protocol: SMTP or ESMTP in RFC 5321, plus names registered with IANA. RFC 3848 registered ESMTPS (STARTTLS negotiated), ESMTPA (SMTP AUTH succeeded), ESMTPSA (both), and LMTP with its A/S/SA variants. The receiving server, describing this hop only.
id An identifier chosen by the receiving server, an atom or a msg-id. RFC 5321: it "MAY contain an '@'… but this is not required." RFC 5321 doesn't define what it identifies. The receiving server. If you operate that host, look the value up in its logs.
for The recipient. RFC 5321: "If the FOR clause appears, it MUST contain exactly one <path> entry, even when multiple RCPT commands have been given." The receiving server. It is optional.
; date-time When this host received the message, in RFC 5322 date-time format. RFC 5321 forbids the obsolete forms, "especially two-digit years". The receiving server's own clock.

Don't read the with keyword as proof of encryption or authentication. RFC 3848 §3 says these keywords "can be modified by an active attacker" and "do not provide any real-world security assurance."

The grammar allows comments (CFWS) between clauses, so implementations add parenthesized free text. The content of those comments isn't standardized, so don't parse it as if it were.

Reading order: bottom to top

Every server prepends its own line, so the lines read newest first:

  • The bottom Received: line is the earliest hop recorded: the first server that accepted the message and wrote a line.
  • The top Received: line is the most recent hop.
  • Compare the by host of each line with the from host of the line above it. They often won't be written the same way, because the from name is the client's EHLO claim, and a host's EHLO name can legitimately differ from the name it writes in by. A mismatch is a reason to look closer, not proof that a hop is missing or a line is false. The IP addresses in the from parentheses are the better thread to follow.

To trace the route, start at the bottom and read up.

Which hops you can trust

Knowing where the lines came from tells you how far to trust them.

  1. A line is only as reliable as the host that wrote it. Any SMTP client can open a connection and write anything into the message content before DATA ends, including text that looks like earlier Received: lines. RFC 5321 §7.1: "it is feasible for even fairly casual users to negotiate directly with receiving and relaying SMTP servers and create messages that will trick a naive recipient into believing that they came from somewhere else."
  2. Your servers preserve whatever they were given. Existing lines "MUST NOT" be changed or deleted, so a receiving server keeps the fabricated lines along with the real ones.
  3. So you can trust only the lines your own receiving infrastructure added. Here "your own" means the receiving side's ADMD, the administrative domain RFC 8601 §1.2 describes as being inside its "trust boundary". Find the lowest line whose by host is one of your own servers. That is the first trusted hop, the moment the message entered your infrastructure.
  4. In the first trusted hop, trust only what your server observed. That means its timestamp, its id, its with protocol, and the IP address in the from parentheses, which came from the TCP connection. The from name before the parentheses came from the client's EHLO and is a claim.
  5. Everything below the first trusted hop can be forged. Lines below it were already in the message when your server accepted it. They may be accurate: a legitimate sender's own relays write accurate lines, and they help you debug that sender's side. But the connecting IP in the first trusted hop is the only address in the chain your infrastructure verified.

The same logic applies when you're the sender reading a copy that reached a mailbox you don't operate. Only the lines added by the receiving provider's infrastructure are the receiver's observations. The lines below them are your own systems' records, which you can check against your own logs.

Per-hop delay

Each timestamp marks when that host received the message. The difference between two adjacent timestamps is how long the message spent between one receipt and the next: queued on the lower host, or in transit from it.

Normalize time zones first

RFC 5321 §4.4 asks servers to use numeric offsets: "SMTP servers that create Received header fields SHOULD use explicit offsets in the dates (e.g., -0800), rather than time zone names of any type. Local time (with an offset) SHOULD be used rather than UT when feasible." RFC 5322 §3.3 defines the offset: "+hhmm means +(hh * 60 + mm) minutes, and -hhmm means -(hh * 60 + mm) minutes." A timestamp of -0000 means "the time was generated on a system that may be in a local time zone other than Universal Time and that the date-time contains no information about the local time zone."

To compute a delay, convert every timestamp to UTC by subtracting its offset (09:00:02 -0400 is 13:00:02 UTC), then subtract adjacent values. If you compare the wall-clock times without converting, a hop can appear to take hours, or even to happen before the hop below it.

Clock skew

Each timestamp comes from the clock of the host that wrote it, and nothing in RFC 5321 requires those clocks to agree. RFC 5321 does say "comparability of Received header fields is important for detecting problems, especially slow relays", which is why the offsets matter. The following is practical reasoning from how the timestamps are produced, not an RFC requirement:

  • A negative delay, where a later hop's timestamp is earlier than the hop below it, means the two clocks disagree. It can't mean the message went back in time. Report the hop as "clock disagreement" rather than as a delay.
  • A small delay between hosts run by different operators can be within the clocks' margin of error. Give more weight to a small delay between two hosts on the same infrastructure.

What a long delay can indicate

A long gap between two hops means the message waited somewhere between those two receipts. Known causes consistent with such a gap:

  • A temporary failure and a retry. After a 4xx reply the sending host keeps the message and tries again later. RFC 5321 §4.5.4.1: "In general, the retry interval SHOULD be at least 30 minutes; however, more sophisticated and variable strategies will be beneficial when the SMTP client can determine the reason for non-delivery." That's a recommendation, not a floor. A deferral retried in line with it leaves a gap of about 30 minutes or more, but a sender using another strategy can retry sooner. See MTA delivery tuning.
  • Greylisting. RFC 6647 describes it as "degradation of service for an unknown or suspect source, over a period of time (typically measured in minutes or a small number of hours)", and notes that "Some popular MTAs do not retry failed delivery attempts for an hour or more." See greylisting.
  • The lower host holding the message in its queue before its first attempt. The header does not record why. See MTA delivery tuning for how sending queues schedule work.

What a long delay does not prove

  • It doesn't tell you the cause. RFC 6647 §3 puts it plainly: "A client does not know, nor is it entitled to know, the reason for the temporary failure status code being returned; greylisting could be in effect, or it could be caused by a local resource issue at the server." The header shows where time passed. The why is in the reply codes and text logged by the host that held the message: check its logs using the id from the line it wrote. See delivery troubleshooting playbooks.
  • It doesn't prove anything below the first trusted hop. A delay between two lines you can't trust may be invented.
  • It says nothing about what happened after the top line. Time between the last Received: stamp and the moment a person saw the message isn't recorded here.
  • It says nothing about folder placement or filtering. Received: lines record acceptance and timing only.

How Received relates to Authentication-Results

The receiver that checked SPF, DKIM, and DMARC records its verdict in an Authentication-Results: field (RFC 8601). Both kinds of field face the same forgery problem, and they share the same solution: rely only on fields added inside your own trust boundary. RFC 8601 §5 allows one exception: a border MTA "MAY elect not to delete" results from an upstream host it explicitly trusts, "even though they exist in completely disjoint administrative boundaries."

  • Authentication-Results behaves like a trace field. RFC 8601 §4.1: it "MUST be treated as though it were a trace header field as defined in Section 3.6.7 of [MAIL] and hence MUST NOT be reordered and MUST be prepended to the message, so that there is generally some indication upon delivery of where in the chain of handling MTAs the message authentication was done."
  • Forged copies are stripped at the border. RFC 8601 §5: an MTA "MUST delete any discovered instance of this header field that claims, by virtue of its authentication service identifier, to have been added within its trust boundary but that did not come directly from another trusted MTA." Section 1.6 explains the reason: "valid use of the header field requires removing any occurrences of it that claim to be associated with the ADMD when the message enters the ADMD."
  • Position can serve as a validity test. RFC 8601 §7.1 lists this among several proposed approaches, none of which it standardizes. The test assumes "internal MTAs are fully compliant" and use their own hostnames or the ADMD's domain as the authserv-id. Under that assumption, "this header field should always appear above a Received header added by a trusted MTA. This can be used as a test for header field validity."
  • Trust is an explicit list, not a default. RFC 8601 §7.1: "It is acceptable to have an MUA aware of this specification but have an explicit list of hostnames whose Authentication-Results header fields are trustworthy; however, this list should initially be empty."

The trust anchor is therefore the receiver that added both your first trusted Received: line and an Authentication-Results: field whose authserv-id it owns. Read that pair together:

  • The Received: line gives the connecting IP it observed.
  • The Authentication-Results: line gives the verdicts it computed for that connection.

An Authentication-Results: field below the first trusted hop, or carrying someone else's authserv-id, is another party's claim. For how authentication results can survive a forwarder, see ARC.

Even a trusted pass is limited in what it tells you. RFC 8601 §7.2: "the existence of this header field indicating a "pass" does not render the message trustworthy."

Worked example

The message below was sent by a customer of example.net's sending platform to a recipient at example.com. Names, IDs and addresses are fictitious; the IPs are from the documentation ranges. The header section is shown top to bottom as it appears in the delivered message, and non-trace fields are omitted:

Return-Path: <bounce-7731@bounce.example.net>
Received: from mx1.example.com (mx1.example.com [192.0.2.25])
        by store.example.com with LMTP id 9KcQ2
        for <reader@example.com>; Thu, 10 Sep 2026 13:31:11 +0000
Authentication-Results: mx1.example.com;
        spf=pass smtp.mailfrom=bounce.example.net;
        dkim=pass header.d=example.net;
        dmarc=pass header.from=example.net
Received: from out1.example.net (out1.example.net [198.51.100.20])
        by mx1.example.com with ESMTPS id 4Q8kx2
        for <reader@example.com>; Thu, 10 Sep 2026 15:31:10 +0200
Received: from submit.example.net (submit.example.net [198.51.100.5])
        by out1.example.net with ESMTP id A71F3;
        Thu, 10 Sep 2026 13:00:03 +0000
Received: from workstation.example.net (workstation.example.net [203.0.113.10])
        by submit.example.net with ESMTPSA id 0e2c9;
        Thu, 10 Sep 2026 09:00:02 -0400

Step 1: read bottom to top and check continuity. The route is workstationsubmit.example.netout1.example.netmx1.example.comstore.example.com. Each line's by host is the next line's from host, so the chain is continuous.

Step 2: find the first trusted hop. The receiving infrastructure is example.com. The lowest line written by an example.com host is the mx1.example.com line. That is the first trusted hop, and the connecting IP it observed was 198.51.100.20. The two lines below it were written by example.net hosts. They are the sending platform's own records: useful to its operators and checkable against their logs, but unverified claims from the receiver's point of view.

Step 3: tie in Authentication-Results. The Authentication-Results: field carries authserv-id mx1.example.com and sits directly above the Received: line mx1 wrote. That matches the position test in RFC 8601 §7.1. Its SPF, DKIM, and DMARC verdicts are mx1's judgement of the message that arrived from 198.51.100.20.

Step 4: normalize timestamps and compute delays.

Hop (written by) Timestamp as written UTC Delay from the previous hop
submit.example.net 09:00:02 -0400 13:00:02 — (first hop)
out1.example.net 13:00:03 +0000 13:00:03 1 s
mx1.example.com (first trusted) 15:31:10 +0200 13:31:10 31 min 7 s
store.example.com 13:31:11 +0000 13:31:11 1 s

Read as local times, the mx1 hop looks 2.5 hours later than out1. The actual gap is 31 minutes, and the message spent them between out1.example.net receiving it and mx1.example.com accepting it.

Step 5: interpret carefully. A 31-minute gap fits a single temporary failure followed by a retry in line with RFC 5321's recommended interval of at least 30 minutes. It also fits greylisting, or queueing on out1. The header alone can't tell these apart. The sending platform's delivery log for queue ID A71F3 on out1 records whether an attempt was deferred and what the reply text said. And since out1's timestamp comes from the sending side, clock disagreement between the two operators is part of the margin.

Checklist

  • Unfold every Received: line, then read from the bottom up.
  • Compare each by host with the from host of the line above it. A mismatch is a reason to look closer, not proof of a missing or false hop.
  • Find the lowest line written by your receiving infrastructure. Trust that line and the lines above it; treat everything below it as a claim.
  • In the first trusted hop, rely on the IP in the from parentheses, not the EHLO name.
  • Convert every timestamp to UTC before subtracting. Treat negative delays as clock disagreement.
  • Use a long delay to locate the host that held the message, then find the cause in that host's logs using the line's id.
  • Read the Authentication-Results: field whose authserv-id belongs to the receiver and that sits above that receiver's own Received: line.

Related articles

#received#headers#trace-fields#smtp#troubleshooting#delays#authentication-results#diagnostics