You did the “grown-up email” work: SPF is green, DKIM verifies, DMARC exists, the headers look clean.
And yet your messages keep landing in the spam folder like they’re auditioning for a life of solitude.
This is the part nobody tells you early enough: authentication is table stakes, not a VIP pass.
Modern inbox placement is a reputation and behavior game, with a side of protocol correctness.
Fast diagnosis playbook (find the bottleneck fast)
When “SPF/DKIM pass but still spam” happens, people panic and start random-walking through DNS.
Don’t. Treat this like an incident. Triage first, then fix.
1) Confirm the problem is placement, not acceptance
- Check if the message is accepted (250 OK) and where it lands: spam vs promotions vs inbox is still delivery, just poor placement.
- If you’re seeing deferrals/blocks (4xx/5xx), you have a reputation or policy gate, not “spam filtering.” Different playbook.
2) Grab one real message and read the headers like a detective
- Authentication: SPF=pass, DKIM=pass, DMARC=pass is necessary, not sufficient.
- Alignment: DMARC “pass” doesn’t always mean what you think if you’re using subdomains, multiple From identities, or third-party senders.
- ARC: If mail is forwarded (mailing lists, ticket systems), ARC can be the difference between “pass” and “looks forged.”
3) Decide which axis is failing: identity, infrastructure, or behavior
- Identity: misalignment, weird From domains, broken Reply-To patterns, inconsistent DKIM selectors.
- Infrastructure: rDNS, HELO, TLS posture, IP/domain reputation, shared IP neighbors, rate patterns, timeouts.
- Behavior: list hygiene, complaint rate, engagement, sudden volume spikes, “cold” domains, content fingerprints.
4) Fix the biggest lever first
- High complaints and poor list hygiene will drown perfect SPF/DKIM.
- Bad rDNS/HELO and sketchy TLS can torpedo you before content is even evaluated.
- Misaligned DMARC can make you look like a phisher with correct signatures.
Paraphrased idea from Werner Vogels: “Everything fails all the time; design for it.” Deliverability is the same—assume distrust, then earn trust repeatedly.
What SPF/DKIM/DMARC actually prove (and what they don’t)
SPF: “this IP is allowed to send for that domain”
SPF checks the envelope sender (MAIL FROM / Return-Path), not the human-visible From header.
SPF passing can coexist with a From domain that has nothing to do with it. That’s why DMARC exists.
DKIM: “this domain signed this content”
DKIM validates that a signing domain (the d= value) signed specific headers and the body.
DKIM says nothing about whether recipients like you.
DMARC: “the visible From aligns with SPF or DKIM”
DMARC is the policy wrapper. It checks whether the domain the user sees in From aligns (strictly or relaxed) with either SPF-authenticated
domain or the DKIM signing domain.
What they don’t prove
- You have a good sending reputation.
- Your list is permission-based and low-complaint.
- Your content isn’t spammy or phishing-adjacent.
- Your infrastructure looks like a real mail system (rDNS/HELO/TLS consistency).
- Your sending patterns look stable and human.
Authentication is like a passport. It proves you are who you claim to be.
It does not prove you’re fun at parties. Inbox providers care about the party.
Interesting facts and historical context (8 quick hits)
- SPF dates to the early 2000s and emerged from a messy era of forged envelope senders and “caller ID for email.”
- DKIM was standardized later after DomainKeys and Identified Internet Mail converged into a single mechanism.
- DMARC came from operators (large mailbox providers and senders) who wanted a policy layer, not just cryptographic proof.
- Alignment was the key innovation: DMARC made the human-visible From domain enforceable.
- Forwarding breaks SPF by design because the forwarder’s IP isn’t authorized in the original domain’s SPF record.
- Mailing lists often break DKIM by modifying subject lines or bodies (footer additions), invalidating signatures.
- Reputation systems predate DMARC: providers have long used IP/domain behavior scoring, even when auth was weak.
- TLS became a deliverability signal not because it’s fashionable, but because opportunistic encryption correlates with legitimate senders.
Joke #1: Email deliverability is the only sport where you can do everything right and still lose to someone else’s “unsubscribe” button.
Practical tasks: commands, outputs, decisions (12+)
These are operator-grade checks. Run them, capture outputs, make decisions. Don’t “feel” your way through deliverability.
Task 1: Inspect SPF record and count DNS lookups
cr0x@server:~$ dig +short TXT example.com
"v=spf1 include:_spf.mailvendor.net ip4:203.0.113.10 -all"
What it means: SPF exists and uses an include plus a direct IP. Good start.
Decision: If you see many includes, flatten or simplify. SPF has a 10-DNS-lookup limit; exceeding it yields permerror and silent pain.
Task 2: Evaluate SPF from a specific sending IP
cr0x@server:~$ spfquery --ip 203.0.113.10 --sender bounce@example.com --helo mail.example.com
pass (sender SPF authorized)
What it means: That IP is authorized for the envelope domain.
Decision: If this returns fail or softfail, fix SPF before anything else. If it returns permerror, you likely exceeded lookups or have syntax issues.
Task 3: Check DKIM selector record exists and is sane
cr0x@server:~$ dig +short TXT s1._domainkey.example.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A..."
What it means: Selector s1 publishes a public key.
Decision: If there’s no record, DKIM will fail. If the record is split across multiple strings incorrectly, some validators choke—publish correctly.
Task 4: Verify DMARC policy and alignment mode
cr0x@server:~$ dig +short TXT _dmarc.example.com
"v=DMARC1; p=quarantine; adkim=r; aspf=r; rua=mailto:dmarc-rua@example.com; ruf=mailto:dmarc-ruf@example.com; pct=100"
What it means: DMARC is set to quarantine with relaxed alignment for SPF and DKIM.
Decision: If you’re passing SPF/DKIM but still spam, DMARC policy is not the lever. Use this to ensure alignment isn’t accidentally failing for some streams.
Task 5: Check rDNS (PTR) for your sending IP
cr0x@server:~$ dig +short -x 203.0.113.10
mail.example.com.
What it means: PTR is set.
Decision: If PTR is missing, generic, or points to unrelated domains, fix it. Many receivers treat missing PTR as a low-trust signal.
Task 6: Verify forward-confirmed reverse DNS (FCrDNS)
cr0x@server:~$ dig +short A mail.example.com
203.0.113.10
What it means: The PTR name resolves back to the same IP.
Decision: If forward and reverse don’t match, align them. This is basic hygiene, and it removes one easy excuse for filtering.
Task 7: Inspect your SMTP banner/HELO name from the outside
cr0x@server:~$ nc -v mail.example.com 25
Connection to mail.example.com 25 port [tcp/smtp] succeeded!
220 mail.example.com ESMTP Postfix
What it means: Banner is a real hostname, not localhost.
Decision: If the banner or HELO is weird, fix Postfix smtpd_banner and myhostname. Inconsistency screams “compromised VM.”
Task 8: Verify STARTTLS and certificate chain
cr0x@server:~$ openssl s_client -starttls smtp -connect mail.example.com:25 -servername mail.example.com -brief
CONNECTION ESTABLISHED
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Verification: OK
Server Temp Key: X25519, 253 bits
What it means: STARTTLS works, modern TLS, cert validates.
Decision: If verification fails, fix the certificate chain. If TLS is absent, add it. This won’t magically inbox you, but it prevents an easy downgrade in trust.
Task 9: Check if you’re on any common DNS-based blocklists (signal, not gospel)
cr0x@server:~$ for z in zen.spamhaus.org bl.spamcop.net b.barracudacentral.org; do \
q=$(echo 10.113.0.203 | awk -F. '{print $4"."$3"."$2"."$1}'); \
echo -n "$z: "; dig +short ${q}.${z} A; done
zen.spamhaus.org:
bl.spamcop.net:
b.barracudacentral.org:
What it means: No listing in those zones (empty output).
Decision: If you are listed, investigate why: compromised host, bad list practices, or shared IP issues. Delisting is paperwork; prevention is engineering.
Task 10: Check Postfix queue for backlog (backlog can look like bursty spam)
cr0x@server:~$ mailq | head
-Queue ID- --Size-- ----Arrival Time---- -Sender/Recipient-------
3F2A01234A* 2480 Mon Jan 4 10:12:01 bounce@example.com
user1@gmail.com
user2@outlook.com
What it means: Messages are queued (asterisk indicates active).
Decision: If the queue grows during sends, you may be rate-limited or greylisted. Slow delivery can cluster retries and create suspicious patterns. Tune concurrency and retry behavior carefully, not aggressively.
Task 11: Inspect SMTP response codes from logs (are you being throttled?)
cr0x@server:~$ grep -E "status=deferred|status=bounced" /var/log/mail.log | tail -n 5
Jan 4 10:14:22 mail postfix/smtp[22101]: 3F2A01234A: to=<user1@gmail.com>, relay=gmail-smtp-in.l.google.com[142.250.27.26]:25, delay=12, delays=0.1/0.02/5/6.9, dsn=4.7.28, status=deferred (host gmail-smtp-in.l.google.com[142.250.27.26] said: 421-4.7.28 [203.0.113.10] Our system has detected an unusual rate of unsolicited mail...)
Jan 4 10:14:25 mail postfix/smtp[22102]: 9ADBC1234B: to=<user2@outlook.com>, relay=outlook-com.olc.protection.outlook.com[52.101.73.8]:25, delay=8.2, delays=0.1/0.01/3.3/4.8, dsn=4.7.0, status=deferred (host outlook-com.olc.protection.outlook.com[52.101.73.8] said: 451 4.7.0 Temporary server error. Please try again later.)
What it means: You’re being rate-limited / reputation-throttled (4.7.x).
Decision: Stop “sending harder.” Reduce volume, segment to engaged recipients, fix complaints, warm up, and ensure you’re not mixing marketing with transactional mail on the same IP/domain.
Task 12: Verify alignment in an actual received header (SPF/DKIM/DMARC results)
cr0x@server:~$ sed -n '1,80p' sample-headers.txt
Authentication-Results: mx.google.com;
dkim=pass header.i=@example.com header.s=s1 header.b=abc123;
spf=pass (google.com: domain of bounce@example.com designates 203.0.113.10 as permitted sender) smtp.mailfrom=bounce@example.com;
dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=example.com
What it means: Alignment is OK: header.from matches DKIM domain and SPF mailfrom is same organizational domain.
Decision: If DMARC fails or shows misalignment, fix your From domain strategy or configure the vendor to sign with your domain and use aligned return-path.
Task 13: Check List-Unsubscribe headers are present (and not broken)
cr0x@server:~$ grep -i "^List-Unsubscribe" -n sample-headers.txt
42:List-Unsubscribe: <mailto:unsubscribe@example.com?subject=unsubscribe>, <https://example.com/unsub?u=abcd>
43:List-Unsubscribe-Post: List-Unsubscribe=One-Click
What it means: You’re offering both mailto and one-click unsubscribe.
Decision: If missing, implement it. If present but your endpoint is flaky, fix reliability first—broken unsubscribe is a complaint factory.
Task 14: Confirm your sending domain is not used for web tracking on a “new” host
cr0x@server:~$ dig +short CNAME click.example.com
tracking.vendor-mail.net.
What it means: Your click tracking uses a vendor domain via CNAME.
Decision: If this tracking domain has bad reputation, it can drag your mail down. Consider disabling click tracking for sensitive streams or moving to a reputable, stable subdomain with consistent history.
Task 15: Check for sudden volume changes by parsing logs
cr0x@server:~$ awk '{print $1" "$2" "$3}' /var/log/mail.log | sort | uniq -c | tail
88 Jan 4 10:12
91 Jan 4 10:13
96 Jan 4 10:14
420 Jan 4 10:15
110 Jan 4 10:16
What it means: There’s a spike at 10:15.
Decision: If spikes correlate with spam placement, smooth the send rate, split campaigns, and ramp gradually. Deliverability hates surprise parties.
Three corporate mini-stories from the trenches
Mini-story 1: The incident caused by a wrong assumption
A mid-sized SaaS company migrated transactional email from a vendor to “in-house Postfix on a clean IP.”
SPF and DKIM both passed in tests. DMARC reported pass. Everyone high-fived and moved on.
Within 48 hours, password reset emails started landing in spam for a non-trivial chunk of users.
Support tickets came in with the classic line: “I’m not receiving emails.” Engineering checked: mail was being accepted (250),
no bounces, no blocks. So it must be “user error,” right?
The wrong assumption was that passing auth plus clean logs equals inbox placement. It doesn’t.
They had switched from the vendor’s warmed-up IP space to a brand-new IP with zero history, then sent a big burst during peak signups.
To mailbox providers, that looks less like “reliable service” and more like “new sender trying to scale fast.”
The fix wasn’t more DNS. It was operational: throttle transactional sends (yes, it hurts), prioritize engaged recipients,
and warm up intentionally. They also separated transactional from marketing streams onto different subdomains and IPs
so one couldn’t poison the other. After a couple of weeks of stable behavior, placement recovered.
Mini-story 2: The optimization that backfired
A retail company got clever. They wanted to reduce bounce processing time and “improve throughput,”
so they tuned their MTA to retry deferred messages more aggressively, increased concurrency, and shortened backoff timers.
On paper: faster delivery. In reality: a slow-motion train wreck.
The major mailbox providers responded with more throttling. Their logs filled with 4.7.x deferrals.
The queue began to oscillate: huge retry bursts, then more deferrals, then even bigger bursts.
What looked like “efficient retries” looked to receivers like a sender that won’t take the hint.
Spam placement worsened across the board, including messages that previously had decent performance.
It wasn’t content. It wasn’t DKIM. It was behavior at the SMTP layer: the sender’s retry pattern became noisy and machine-like.
The rollback was boring: restore conservative retry schedules, reduce concurrency, and introduce per-domain rate limiting.
They also started treating 4.7.x as a signal to slow down, not a challenge to fight.
The irony: delivery got faster for the recipients who mattered, because the senders stopped triggering throttles.
Mini-story 3: The boring but correct practice that saved the day
A healthcare-adjacent company had strict deliverability requirements for appointment reminders.
They did something uncool: they ran weekly list hygiene jobs, processed bounces within hours,
and kept a suppression list that nobody was allowed to override without a ticket and an audit note.
One quarter, their marketing team imported a “reactivation list” from an old CRM export.
It wasn’t malicious. It was just stale. The first send produced a wave of hard bounces and a noticeable uptick in complaints.
Here’s where the boring practice saved them: automated suppression kicked in immediately, removing dead addresses before the second wave.
Their systems also flagged the complaint spike and paused further sends pending review. Transactional mail continued on separate infrastructure.
The aftermath was mild: a small temporary dip in placement for marketing messages, but zero impact on reminders.
No emergency DNS changes, no frantic “SPF flattening,” no vendor blame. Just disciplined hygiene and separation of concerns.
Common mistakes: symptom → root cause → fix
1) Symptom: SPF/DKIM pass, DMARC pass, but Gmail says “similar messages were identified as spam”
Root cause: poor engagement and/or complaints, often from sending to cold lists or mixed streams (transactional + marketing).
Fix: segment to engaged recipients, pause cold segments, separate marketing and transactional on different subdomains/IPs, add list-unsubscribe, and reduce frequency.
2) Symptom: Outlook/Hotmail places mail in junk intermittently
Root cause: inconsistent infrastructure signals (rDNS/HELO mismatch, flaky TLS, variable IPs), plus content patterns that resemble phishing.
Fix: stabilize sending IPs, fix rDNS and EHLO, ensure STARTTLS works consistently, align From/Reply-To/link domains, and avoid risky attachment types.
3) Symptom: Internal tests show inbox, real users see spam
Root cause: seed tests are not representative; real-user behavior drives reputation. Also, consumer inboxes differ by user history.
Fix: instrument complaint rate, bounce rate, and engagement by domain; test with real segments; focus on recipients who opted in recently.
4) Symptom: DMARC fails only for forwarded mail
Root cause: SPF breaks on forward; DKIM breaks when intermediaries modify content; ARC missing.
Fix: ensure DKIM survives (sign stable headers, avoid rewriting), consider ARC on forwarders you control, and accept that some forwarding will be lossy.
5) Symptom: DMARC passes but brand still looks suspicious
Root cause: misaligned “brand surface area” (Reply-To or links on unrelated domains; heavy tracking; URL shorteners).
Fix: keep From/Reply-To/links within a tight, consistent domain family; minimize redirects; use a stable tracking subdomain with history.
6) Symptom: Sudden spam placement after switching ESPs
Root cause: new sending IP/domain stream with no reputation; volume ramp too steep; old suppression list not migrated.
Fix: warm up gradually, migrate suppressions, start with engaged recipients, and avoid changing From domains at the same time as infrastructure.
7) Symptom: Deliverability fine, then collapses after adding a new template
Root cause: content fingerprint trip (phrasing, link patterns, or a domain in URLs with bad reputation).
Fix: diff templates, remove risky phrases, verify URL domains, reduce image-to-text imbalance, and avoid attachments unless necessary.
8) Symptom: Messages are delayed heavily, then arrive in spam
Root cause: throttling and retry storms create burst patterns; queue backlog changes timing and reputation perception.
Fix: implement per-domain rate limiting, honor deferrals, reduce concurrency, and smooth sending.
Checklists / step-by-step plan
Step-by-step plan for “auth passes but spam” (do this in order)
-
Pick one representative message that went to spam and save full headers.
- Decision: if you can’t reproduce with real headers, you’re debugging vibes.
-
Confirm DMARC alignment for that message (header.from vs DKIM d= and SPF mailfrom).
- Decision: if misaligned, fix identity architecture before anything else.
-
Check infrastructure hygiene: PTR, FCrDNS, HELO, TLS.
- Decision: fix any obvious hygiene issues; they’re cheap wins and reduce baseline suspicion.
-
Check for throttling/deferrals in logs (4.7.x is your canary).
- Decision: if throttled, slow down and segment; don’t tune retries to “win.”
-
Separate streams: transactional vs marketing vs notifications.
- Decision: if you mix them today, plan the split. It’s one of the highest ROI changes you can make.
-
Audit list hygiene: bounces, complaints, old segments, reactivation campaigns.
- Decision: if you can’t measure complaints/bounces quickly, fix instrumentation before scaling sends.
-
Reduce “suspicious surfaces”: mismatched domains, aggressive tracking, URL shorteners, attachments.
- Decision: simplify. You’re not trying to win a marketing tech award; you’re trying to be delivered.
-
Warm up and stabilize: consistent volume, predictable schedule, gradual ramps.
- Decision: treat warming as a production rollout, not a one-time config task.
Operational checklist (weekly)
- Review bounce and complaint trends by recipient domain.
- Confirm suppressions are applied within hours, not days.
- Rotate DKIM keys on a controlled schedule (and keep old selectors during transition).
- Validate rDNS and TLS remain correct after infrastructure changes.
- Review top clicked domains and redirect chains used in email templates.
- Check queue depth and deferrals; tune rate limits, not just concurrency.
Architecture checklist (one-time, then maintain)
- Separate sending subdomains per stream (e.g.,
notify.,billing.,marketing.). - Align DKIM d= with the From domain (or organizationally aligned subdomain) whenever possible.
- Use a dedicated IP (or at least dedicated pool) for critical transactional messages.
- Implement List-Unsubscribe and one-click unsubscribe for bulk mail.
- Ensure bounce processing is automated and reliable.
FAQ
1) If SPF and DKIM pass, why would a provider still send me to spam?
Because authentication only proves identity and message integrity. Placement is driven by reputation, behavior, engagement, complaints, and content patterns.
Passing SPF/DKIM just means you’re a verified sender—not a welcomed one.
2) Is DMARC required for inbox placement?
Not universally, but practically yes if you care about consistent delivery and preventing spoofing. DMARC also forces you to clean up alignment,
which removes a whole category of “this looks forged” signals.
3) What’s the single fastest improvement if we’re going to spam?
Stop sending to unengaged recipients. Segment aggressively: recent opens/clicks/replies (whatever you can reliably measure),
recent signups, and recipients who have interacted with you. Reputation recovers faster when you stop annoying people.
4) Should we move to a dedicated IP?
If you send enough volume to justify warming and maintaining it, yes—especially for transactional streams.
If your volume is low or spiky, a dedicated IP can be worse because you can’t build stable reputation.
5) Does content really matter if our reputation is good?
Yes. Reputation gets you in the door; content decides whether you stay in the room. Phishy structure, risky URLs,
heavy tracking, and odd formatting can sink otherwise reputable mail.
6) Our mail is forwarded through ticketing systems and then fails. What do we do?
Forwarding breaks SPF, and modifications break DKIM. If you control the forwarder, consider implementing ARC.
Otherwise, focus on DKIM robustness (sign stable headers) and accept that some forwarding scenarios reduce trust.
7) We added more DKIM keys/selectors. Could that hurt deliverability?
Not by itself. What hurts is inconsistent signing (sometimes signed, sometimes not), broken DNS records, or selectors being rotated without a transition period.
Keep old selectors active while mail still in transit can reference them.
8) Should we use “p=reject” on DMARC to improve deliverability?
DMARC enforcement can reduce spoofing and brand abuse, which indirectly helps reputation.
But it won’t fix bad list hygiene or high complaints. Don’t treat p=reject like a deliverability button.
9) Why do seed tests say “inbox” but real users see spam?
Seed accounts don’t behave like real recipients and don’t have the same history. Providers personalize filtering.
Your actual audience’s behavior (deletes, complaints, lack of reads) is the truth serum.
10) Are DNS blocklists the reason we’re in spam?
Sometimes, but not usually the whole story for consumer inboxes. Blocklists are better at explaining hard blocks than subtle spam placement.
Use them as a signal to investigate compromise or bad sending patterns, not as the only metric.
Conclusion: next steps that actually move the needle
If SPF and DKIM pass but you’re still going to spam, stop treating deliverability like a DNS puzzle.
It’s a trust system. Trust is earned through consistent identity, clean infrastructure, and respectful sending behavior.
Do this next, in order:
- Pull one spammed message, save full headers, and confirm DMARC alignment and domain consistency (From/Reply-To/links).
- Fix infrastructure hygiene: rDNS, FCrDNS, HELO, and STARTTLS with valid certs.
- Check logs for throttling (4.7.x). If you see it, slow down and segment immediately.
- Separate transactional from marketing streams by subdomain and ideally IP/pool.
- Implement and test one-click unsubscribe; tighten bounce handling and suppress hard bounces fast.
- Warm up new IPs/domains like you would roll out a new database cluster: gradually, observably, and with rollback plans.
The hidden signals aren’t really hidden. They’re just not in your DNS zone file.
They’re in your logs, your audience behavior, and the boring consistency of your systems.