Email Header Analyzer
Support engineers tracing where a message stalled, deliverability specialists checking what the receiving server wrote down for SPF, DKIM and DMARC, and developers debugging a webhook that sends mail all work from the same raw text: the header block sitting above the blank line of a message. Paste it here and the tool unfolds continuation lines, orders the Received hops from the sending host to the mailbox, measures the gap between each pair of timestamps, pulls the Authentication-Results entries, and lists what looks wrong with the path. Parsing runs in your browser, so nothing you paste leaves the page.
—
—
How the Tool Reads a Pasted Header Block
Header lines are name-colon-value, and any line that begins with a space or tab continues the value of the line above it, so a five-line Received block is still one header. The tool applies the RFC 5322 unfolding rule first, joining continuation lines back onto their parent with a single space, and only then splits the block into name and value pairs at the first colon of each line. Each Received value is cut at the last semicolon: the part before it describes the hop with from, by, with, id and for, and the part after it is the arrival timestamp that server stamped. Both halves are kept, which is what makes a hop readable as one line instead of a folded wall of text.
Received lines are newest-first in the source because every server prepends its own line before passing the message along. The tool reverses them into transport order and subtracts each arrival time from the next, producing a gap in milliseconds for every hop after the first. Authentication-Results, or Arc-Authentication-Results when an intermediate system re-signed the message, is read after its first semicolon and split on semicolons into method and result pairs such as dkim=pass or dmarc=none, with the leftover text kept as parameters like header.d and smtp.mailfrom. From DKIM-Signature the tool pulls d= and s=, from X-Spam-Status it pulls score= and tests=, and X-Spam-Flag: YES is treated as a separate hard signal.
Why the top Received line is the last hop
Each server that accepts the message prepends a Received line, so the line nearest the top was written by the server closest to the mailbox. Reading down the raw source therefore walks from delivery back toward submission, and you can prove it to yourself: the by= host on one line usually matches the from= host on the line above it. The tool flips the list once after unfolding, so the trace reads sender first and recipient last with timestamps increasing as you go down.
What each gap is measuring
A gap is computed only between two hops whose timestamps both parse and where the later one is not earlier than the first; the first hop in the trace has no gap because nothing precedes it. One to ten seconds is ordinary store and forward. Past a minute you are looking at queueing, greylisting or a content scan holding the message, and an issue is raised once the slowest gap passes 90 minutes. A negative number means one server clock runs ahead of another and is flagged as a warning rather than dropped.
Before and after: three Received lines as raw text and as an ordered trace
Received: from mx2.acme.io (mx2.acme.io [198.51.100.20])
by mx.acme.io with ESMTPS id A1b2C3d4e5f6
for <alex@acme.io>; Wed, 14 Oct 2026 09:41:07 +0000
Received: from filter3.acme.io (filter3.acme.io [198.51.100.13])
by mx2.acme.io with ESMTPS id B2c3D4e5f6g7
for <alex@acme.io>; Wed, 14 Oct 2026 09:41:05 +0000
Received: from mail-out.example-shop.com (mail-out.example-shop.com [203.0.113.42])
by filter3.acme.io with ESMTP id C3d4E5f6g7h8
for <alex@acme.io>; Wed, 14 Oct 2026 09:40:58 +0000 1. mail-out.example-shop.com -> filter3.acme.io | via ESMTP | id C3d4E5f6g7h8 | Wed, 14 Oct 2026 09:40:58 +0000
2. filter3.acme.io -> mx2.acme.io | via ESMTP | id B2c3D4e5f6g7 | Wed, 14 Oct 2026 09:41:05 +0000 (+7s)
3. mx2.acme.io -> mx.acme.io | via ESMTPS | id A1b2C3d4e5f6 | Wed, 14 Oct 2026 09:41:07 +0000 (+2s) Use Cases
| Situation | What to paste | What to read first |
|---|---|---|
| Ticket: a customer says the notice arrived an hour late | Headers from the copy the customer received | Slowest gap chip, then the hop trace, the long pause names the relay that queued it |
| Deliverability check for a new sending domain | Headers from a test message sent to a seed inbox | Authentication results panel, then SPF, DKIM, DMARC chips and the header.d value |
| Webhook sender drops mail or bounces it | Headers from the bounced or delayed message | Issues list, especially missing Message-ID, From vs Return-Path domains, repeated by= hosts |
Common Mistakes
- Copying only the short header pane a desktop client shows. Most clients hide the Received chain and the Authentication-Results line there, so open the full message source and paste everything above the blank line.
- Reading the Received block top-down as if it were chronological. The top line is the last hop, and treating it as the first makes every gap look backwards; use the reversed trace instead.
- Trusting X-Spam-Score on its own. Quarantine thresholds differ per gateway, 5.0 is the common one and some start at 3.5, while X-Spam-Flag: YES is the signal that a filter already made up its mind.
- Assuming dkim=pass means the visible From address was signed by that key. Compare header.d with the From domain, because a valid signature from a sending platform does not cover a spoofed display address.
- Treating a negative hop gap as a bug in the tool. It means one server clock runs ahead of the previous hop, which happens across providers every day and is worth naming when you ask a relay operator about a delay.
- Ignoring a missing Message-ID or Date header. Threading and duplicate detection break without Message-ID, and clients plus filters show the wrong send time or score the message down when Date is absent.
Best Practices
- Paste the entire header block, including lines that look like noise. Delivered-To, X-Original-Received and the ARC set often explain a path that was rewritten by a forwarder.
- Compare the Return-Path envelope domain with the From domain before debugging a DMARC problem. The two differ on bounce addresses by design, and the receiving server checks both.
- Keep the raw headers with the ticket. Results come from the server that delivered the message, so re-opening the same message in another client later can show a different set of Received lines.
- Read the Authentication-Results written by the final receiving MX. An intermediate filter evaluated the message before the last relay, and its opinion may not match what the mailbox provider recorded.
- When a gap is long, take the queue id from that Received line and search your mail logs for it. Postfix and Exim both record queue runs against the id, which tells you what the relay was waiting on.
- Re-run the same paste after publishing new DNS records. SPF, DKIM and DMARC results are decided at delivery time, so only a fresh test send shows whether the fix took effect.
Frequently Asked Questions
How do I copy the full headers from my mail client?
Gmail: open the message, press the three-dot menu, choose Show original, then Copy to clipboard. Outlook on the web and the desktop app: choose View message source, or in older versions Internet headers, and copy everything in the panel. Apple Mail: select the message, choose File > Get Info, then copy the headers section. Paste all of it here, including everything above the blank line that separates headers from the body, because the parser stops reading at that blank line.
Why does the tool reverse the order of Received headers?
The topmost Received line was written last, by the server that delivered the message into the mailbox you are reading, and every line below it was written earlier in the path. Transport order reads origin first and destination last, which is the direction you actually reason about when you ask where a message went. Reversing the list also makes the timestamps run forward, which is what lets the tool subtract one hop time from the next. Read top-down in the raw source and you are walking backwards from delivery to submission.
What does a gap between two hops mean?
A gap is the difference between the arrival timestamp of one hop and the arrival timestamp of the next one after it. One or two seconds is normal: the server accepted the message, scanned it, and retransmitted it straight away. Tens of minutes usually point at a relay queue, greylisting where the sending address must be retried, or a content scan holding the message back. Gaps can also come out negative when two servers disagree about their clocks, and the tool reports that instead of hiding it, because clock skew is worth raising with the operator of that hop.
How do I know whether SPF, DKIM and DMARC passed?
Look at the Authentication-Results header. Each method listed there carries a result: pass, fail, softfail, permerror, or none, and pass is the only result that counts as authenticated. The arguments after the result matter as much as the result itself: header.d and header.s show which DKIM domain and selector signed the message, while smtp.mailfrom shows the envelope sender that SPF evaluated. A message can show dkim=pass and spf=pass while still showing dmarc=none when the envelope domain does not match the From domain.
What if there is no Authentication-Results header at all?
That means the receiving server did not record one, or an intermediate system stripped it while relaying the message. Nothing in the pasted block then tells you what the filters decided, so any pass or fail you infer from another source is unverified. Check for ARC headers instead: Arc-Authentication-Results, Arc-Message-Signature and Arc-Seal are added by forwarders that re-sign a message they relayed. You can also look for a DKIM-Signature header, which shows the sender signed the message but says nothing about whether the signature checked out.
How can I spot a mail loop in a header trace?
The clearest sign is the same host appearing as the by= receiver in more than one Received line. The tool counts by= hosts and raises a warning naming the host and how many times it shows up. A loop also tends to produce a long chain, and anything at eight hops or more is flagged as a long path because every extra relay adds latency. If the repeated host belongs to your own infrastructure, look for a transport rule that re-injects the message or an alias that points back at itself.
Does this tool read my mail?
No. You paste the headers yourself, and the parsing runs locally in your browser using plain string operations. The script makes no request carrying your header text, nothing is written to localStorage or cookies, and closing the tab drops the text. You can confirm it by disconnecting from the network after the page loads and then typing in the textarea: the trace, results and issues keep updating.