Skip to content

Why your email goes to spam, in the order worth checking

A diagnostic order rather than a list of tips: authentication, then reputation, then list quality, then content. Includes a parser for the Authentication-Results header.

DeliverabilityIntermediate14 min readUpdated

01Start with the headers, not with the content

Start here, always. The receiver already told you what it concluded about your authentication, in a header, in plain text, on the message that went to spam. Reading it takes thirty seconds and it eliminates or confirms the entire first layer of the diagnosis before you have formed a single theory.

TL;DR

Find the Authentication-Results block. It was added by the receiving server, which makes it the only part of the message your sending infrastructure could not have written.

ClientHow to see the source
GmailOpen the message, then Show original
OutlookView message source
Apple MailView → Message → All Headers

The block will be near the top.

AUTHENTICATION-RESULTS

A local parse. Nothing you paste leaves your browser — this component makes no network request of any kind.

In Gmail: Show original. In Outlook: View message source.
DKIMpass
The check succeeded.
[email protected] header.s=ms1 header.b=Ab3dEf
SPFpass
The check succeeded.
(google.com: domain of [email protected] designates 1.2.3.4 as permitted sender) [email protected]
DMARCpass
The check succeeded.
(p=REJECT sp=REJECT dis=NONE) header.from=yourdomain.com

DMARC is the one that decides. A message can show spf=pass and still fail DMARC, because DMARC additionally requires that the passing domain matches the domain in the From header a reader sees.

What you readWhat it means
spf=passThe connecting server was authorised by the envelope sender’s domain — which is not necessarily yours.
dkim=passThe signature verified against a key published in the signing domain’s DNS.
dmarc=passOne of those two passed and aligned with the From domain. Three passes mean your authentication is not the reason.
noneNot a failure. No record was published, so nothing was checked — which is a different problem from failing, and a worse one at DMARC.
permerrorA syntax problem in your own DNS, most often two SPF records on one name or an SPF record that exceeds the ten-lookup limit. Receivers treat both as no SPF at all.

none and permerror are the two results people consistently misread.

02Authentication failures

The reason authentication goes first is not that it is the most common cause. It is that it is the cheapest and most certain: the answer is binary, it comes from the receiver rather than from your inference, and the fix is fully within your control and finishable this afternoon. Everything below this layer is probabilistic and slow. Never start with the slow, fuzzy layer while a fast, certain one is unresolved.

LayerKind of answerTime to fixWhy it sits here
AuthenticationBinaryAn afternoonEither the header says pass or it does not. No judgement, no negotiation, and the fix is entirely in your DNS.
ReputationContinuousWeeksA rolling history of how recipients reacted to you. Cannot be argued with, only outlived.
List qualityMeasurableOne sendComplaint rate, unknown-user rate, engagement. You control the inputs directly and the effect is immediate.
ContentFuzzyUnknowableThe only layer where you cannot tell whether a change helped. Which is why it goes last.

The failure that catches most people is not a missing record. It is alignment. DMARC does not ask whether SPF passed; it asks whether SPF passed for a domain that matches the From header. A message with From: [email protected] and Return-Path: [email protected] shows spf=pass in the header and still fails DMARC, because what passed was the vendor’s domain and it proved nothing about yours.

From:        [email protected]      ← what the reader sees
Return-Path: [email protected]        ← what SPF checked: not you
DKIM d=      yourdomain.com           ← aligned: DMARC passes on this alone

This is why the bounce path belongs on a subdomain of your own domain rather than the transport’s, and why DKIM is worth publishing even when SPF already passes — DKIM alignment is the one that survives a forward. If any of that is unfamiliar, the authentication guide builds the records, and verification tells you whether what you published is what resolvers see.

HeaderWhat MailySend sends on every message
List-UnsubscribeBoth an HTTPS and a mailto option
List-Unsubscribe-PostList-Unsubscribe=One-Click

Transactional mail included. The large consumer providers require this from bulk senders, and it is checkable in the same header dump you already have open.

03Domain and IP reputation

Reputation is a rolling summary of how recipients behaved towards your mail, kept per-domain and per-IP by each receiver, and never shown to you. There is no score to look up, no appeal, and no lever. There is only the behaviour that produces it, which means the only way to change a reputation is to send different mail for long enough that the old mail falls out of the window.

TL;DR

Domain reputation is the one that matters most now, and it travels with you: changing transports does not reset it, because it is attached to the name in the From header and the DKIM d= domain, not to the machine that connected.

That is the point of DMARC alignment from the receiver’s side — it makes reputation attributable to a party that cannot walk away from it.

A dedicated IP, above ~100,000 a month
  • Worth it above roughly 100,000 messages a month, sent on a steady schedule
  • The threshold is about consistency as much as total — 100,000 a month in one monthly blast does not qualify
A dedicated IP, below that
  • Actively worse than a well-run shared pool
  • Reputation is built from volume observed over time, and an IP that sends 4,000 messages one week and nothing the next never accumulates enough history for any receiver to form an opinion
  • An IP with no reputation is treated with suspicion, not neutrality

04List quality beats content every time

This is where most spam problems actually live, and it is three numbers. None of them is about your writing. Receivers weigh complaint rate, unknown-user rate, and engagement, in roughly that order of severity, and they weigh them per sending domain over a rolling window.

0.3%The complaint rate the large providers name — a ceiling, not a target
0.1%Healthy is under this
3 in 1,000People reporting you: enough to change your placement everywhere

Complaint rate is the proportion of delivered messages where a recipient pressed the spam button, and at the number above you are already in trouble. That is why a hard-to-find unsubscribe link is such a bad trade: the alternative to unsubscribing is not staying subscribed, it is complaining.

Unknown-user rate is the proportion of messages rejected because the address does not exist. A sender who repeatedly mails addresses that never existed is a sender who bought, scraped or typo-collected a list, and receivers read it exactly that way — which is why hard bounces must suppress immediately and permanently.

BounceClassSuppressed for
5.1.1, 5.1.3, 5.1.6hard_invalidPermanently
5.1.2hard_domainPermanently
Mailbox fullSoft7 days
ThrottledSoft1 day
ContentSoft3 days
TemporarySoft2 days
An unrecognised diagnosticunknownNot at all, deliberately

Eight classes in total, mapped from the enhanced status codes rather than guessed from the text. Not suppressing on unknown is the right trade in one direction only: an unrecognised bounce is a gap in the classifier, and guessing costs a real subscriber permanently — so a rising unknown count is a signal about the classifier, not about your list.

Engagement is opens, clicks, replies, moves out of spam, and — most strongly — the absence of any of these over a long window. A segment that has not opened in a year is not neutral weight; it is negative weight, and continuing to mail it drags the placement of the mail your engaged recipients do want. Suppressing your dormant third is the single highest-leverage deliverability action available to most senders, and it feels like giving up revenue right until placement recovers.

Open classification: what runs in production today
  • HEAD-request detection
  • The security-vendor user-agent list
  • Apple MPP user-agent matching
  • The bot list, and empty user agents
What never runs
  • The timing-window rule
  • The ASN rule
  • The multiple-links-in-two-seconds rule
  • Because the tracking endpoint passes only user agent, IP, method and country to the classifier

05Content, last and least

Content is last because it is the layer with the worst ratio of effort to certainty. Per-word scoring — the “spam words” lists, the idea that writing free or guarantee in a subject line trips a filter — has not been the dominant signal for many years. Filters are trained on sender behaviour and recipient reaction, and a sender with a clean complaint rate can write free as often as they like.

TL;DR

The content factors that still matter are structural rather than lexical: whether the message is shaped like ordinary mail, and whether the domains it points at are trustworthy.

The folklore persists because it is actionable and the real causes are not. Rewriting a subject line takes five minutes and feels like progress; suppressing 40% of your list takes an argument with whoever owns the revenue number. The five minutes is the reason people spend a week on the wrong layer.

Still mattersWhy
A plain-text alternativeA multipart message with a real text part is normal mail. HTML-only is a weak negative on its own and a strong one in combination.
Link domainsLinks to a domain with a bad reputation, or through a public URL shortener shared with every phishing campaign on earth, are judged separately from your sending domain.
The tracking domainOpen and click tracking rewrite links through a domain. If that domain is shared and unbranded, its reputation is not yours to control.
One giant imageA message that is a single image with no text is unreadable to a filter, to a screen reader, and to anyone with images off. All three read it the same way.
A visible unsubscribeNot for the filter — for the complaint rate. Every hidden unsubscribe link converts a would-be unsubscribe into a spam report.

MailySend generates the text alternative from your HTML on render, inlines the CSS and enforces a table layout for the same reason: those are the properties of ordinary mail, and looking ordinary is the whole content strategy.

1
Header
the receiver already answered it
2
Reputation
a volume step change explains more sudden spam placement than everything else combined
3
List quality
the three numbers are the actual inputs to the filter
4
Content
last, because you will never know whether the change worked

What just happened

You have an order to work in rather than a list of tips: settle authentication from the header, then look at your complaint and unknown-user rates, and only then argue about the subject line. The thing most likely to waste your week is skipping to the last step, because content is the only layer where you cannot measure whether a change helped — rewriting an email until it feels less spammy is unfalsifiable work, and the rate that actually moved your placement was the handful of recipients per thousand who pressed the spam button.

Common questions

Read next

YOUR ACCOUNT, YOUR MAIL

Nothing to sign up for. Just deploy it.

Every guide on this site describes software you run yourself.