Every message carries a record of where it has been, written by each server that handled it. It answers the two questions that otherwise become guesswork: where the delay happened, and whether the sender was genuine.
Finding them
In Roundcube, open the message and choose to view the source. In Outlook, message properties. In Gmail, "show original".
If you are asking someone else to send you a message for analysis, ask them to forward it as an attachment. Ordinary forwarding rewrites the headers and destroys exactly what you needed.
Received: read from the bottom
Each server adds its line at the top, so the oldest entry is at the bottom. The bottom-most line is where the message originated; the top is your own server.
Two things come out of these lines.
Where a delay happened. Compare timestamps between consecutive lines. A four-hour gap between two hops names the server that held it. If the gap is at the far end, the delay was not yours, which is a useful thing to be able to state with evidence.
Where it actually came from. The bottom entry shows the originating address, which is frequently nothing like what the visible sender suggests.
Authentication-Results: the honest answer
Authentication-Results: mx.example.com; spf=pass smtp.mailfrom=sender.com; dkim=pass header.d=sender.com; dmarc=pass
This is the line that says whether the message really came from the domain it claims.
spf=pass means the sending address was authorised by that domain. dkim=pass means the signature validated and the content was not altered. dmarc=pass means the domain's own policy was satisfied.
A message claiming to be from a bank with spf=fail is forged, and this is where you can say so definitively rather than by impression. Understanding SPF, DKIM and DMARC deals with what those checks actually test.
From: proves nothing
The visible sender is written by whoever composed the message. It can say anything.
This is the whole mechanism behind messages that appear to come from a colleague or a supplier. The address is displayed; nothing about displaying it verifies it.
Return-Path is the address bounces go to, and it frequently differs from the visible sender in forged mail. A mismatch between the two is a useful early signal, though legitimate mailing services also differ here.
Message-ID: what to search the log for
A unique reference for the message. When tracing on your own server:
grep 'the-message-id' /var/log/exim_mainlog
That shows what your server did with it: accepted, rejected, queued, delivered, which distinguishes "we never received it" from "we received it and the recipient's system discarded it". Those are entirely different problems with entirely different fixes.
Exim configuration and mail routing deals with reading the log.
Spam scores
Filtering systems add their own headers with a score and the rules that contributed.
When your own mail is being filtered, this is the most direct diagnosis available: it lists exactly which checks counted against the message. Frequently it is a missing authentication record rather than anything about the content, which is a fixable configuration problem instead of a writing problem.
Ask a recipient whose system filtered your message to send you the headers. It is faster than any amount of guessing. Understanding bounces and delivery codes walks through the case where it was rejected outright instead.
Two habits worth having
When someone reports a suspicious message, ask for it as an attachment and read the authentication line before anything else. It settles most cases in seconds.
When someone reports a delay, read the received lines before assuming the server is at fault. A large share of "your mail server is slow" reports turn out to be a delay at the other end, and the timestamps say so plainly. The email troubleshooting guide sets out working through the rest.
Read the delivery path for timing
The received lines carry timestamps, and the gaps between them are where a delay actually happened.
grep -i '^Received:' message.eml | tac | head -10 grep -iE '^(Date|Received):' message.eml | head -12
Read from the bottom upwards. Each line was added by a server as the message passed through, so the earliest is at the bottom and the most recent at the top.
A large gap between two consecutive lines names the system that held the message. That is the answer to the most common question about mail, which is why something took four hours, and it is visible in the headers rather than requiring anybody's logs.
Match the message to your own logs
The identifier in the headers is what connects a customer's complaint to a record on your server.
grep -i '^Message-ID:' message.eml
grep -F "$(grep -i '^Message-ID:' message.eml | cut -d' ' -f2 | tr -d '<>')" /var/log/exim_mainlog | head
exim -Mvl "$(grep -oE '[0-9A-Za-z]{6}-[0-9A-Za-z]{6}-[0-9A-Za-z]{2}' /var/log/exim_mainlog | head -1)" 2>/dev/null
Searching the mail log for the identifier finds every line about that specific message, including the acceptance, the delivery attempt and the response from the receiving server.
That response is the useful part. It is the receiving side's own words about why the message was refused or deferred, which is more precise than any bounce message the sender sees.
Recognise the headers that indicate forgery
The visible sender is decoration, and a small number of fields tell you whether it was really sent by whom it claims.
grep -iE '^(Authentication-Results|Received-SPF|DKIM-Signature|Return-Path|Reply-To|From):' message.eml
Three mismatches are worth recognising. A return path on a different domain from the visible sender. A reply address that differs from the sender. And an authentication result showing a pass for a domain that is not the one displayed.
The last is the subtle one, because a check can pass legitimately for the sending infrastructure while the displayed name is fabricated. Reading which domain passed, rather than that something passed, is the whole distinction. Stopping your domain being used for spoofing covers the protection.