Email deliverability: Messages land in spam — the real fixes (SPF/DKIM/DMARC done right)

Was this helpful?

You shipped the email. The SMTP logs say “250 2.0.0 OK”. Your app marks it “delivered”. Sales still swears the customer never got it. And when you ask for a screenshot, it’s there—in the spam folder—sitting between a fake invoice and a “CEO wants gift cards” special.

This isn’t a mystery. It’s an engineering problem with a reputation system attached. SPF/DKIM/DMARC are table stakes, but most teams configure them like a checkbox and then act surprised when Gmail treats them like a checkbox too.

Fast diagnosis playbook

If you’re on-call and your CEO just forwarded “why are we in spam?” to the entire thread, don’t start by rewriting your SPF record from memory. Triage like an SRE: identify the bottleneck fast, then narrow.

First: confirm what the receiver saw

  • Get the full message source from the recipient mailbox (headers matter more than your logs).
  • Find Authentication-Results and Received headers. They tell you SPF/DKIM/DMARC outcomes as evaluated by the receiver—the only verdict that counts.
  • Check which domain is in the visible From: and which domain is in the Return-Path (bounce address). DMARC is an alignment game.

Second: identify the failure mode

  • SPF fail/softfail/permerror? Usually DNS, too many lookups, or the wrong sending IP.
  • DKIM fail? Usually selector mismatch, broken canonicalization due to rewriting, or the wrong key published.
  • DMARC fail? Usually alignment (From domain doesn’t align with SPF or DKIM-authenticated domain), or policy/reporting misconfiguration.
  • All pass but still spam? Then you’re in reputation/content/engagement territory, or you’re hitting throttling/greylisting and arriving “weird”.

Third: check your identity and transport hygiene

  • Reverse DNS (PTR) for the sending IP must exist and look sane.
  • HELO/EHLO name should match a valid hostname that resolves forward and ideally aligns with PTR.
  • TLS and cipher aren’t deliverability magic, but broken STARTTLS negotiation is a trust smell.
  • Bounces and complaint rates are your “blood pressure”. If you’re high, no amount of DNS yoga saves you.

Then: pick the shortest fix path

  • If SPF is broken: fix SPF without causing DMARC misalignment.
  • If DKIM is broken: fix selector/key publication, then sign at the final outbound hop.
  • If DMARC alignment is broken: decide whether SPF alignment or DKIM alignment is your primary path; design accordingly.
  • If everything authenticates: work on reputation and list hygiene; stop blasting cold lists from a fresh domain.

Paraphrased idea from Werner Vogels: “Everything fails; build systems that assume it and recover quickly.” Email deliverability is exactly that, except the failure is silent and your customers don’t file tickets—they just don’t buy.

How spam filters actually decide (beyond SPF/DKIM/DMARC)

SPF, DKIM, and DMARC answer one question: “Is this message plausibly authorized by the domain in the From header?” They do not answer: “Is this message wanted?”

Modern mailbox providers run a layered scoring system. Some of it is rules. Much of it is reputation. A lot is “user feedback without telling you,” like when users delete your email without reading or mark it as spam. The uncomfortable truth: deliverability is a product feature that you earn and maintain, not a config snippet.

The signals that matter in practice

  • Authentication + alignment: SPF/DKIM pass is not enough; DMARC alignment is the gatekeeper for many inboxes.
  • Sending infrastructure consistency: stable IPs, consistent HELO, consistent envelope sender domains.
  • Complaint rates: if people hit “spam,” you’re done for a while. Providers don’t debate. They throttle or junk.
  • List hygiene: old lists rot. Spam traps exist. Hard bounces are not “temporary sadness,” they’re a reputation tax.
  • Content patterns: phishing-like language, broken HTML, link shorteners, mismatched display text vs URL, weird encodings.
  • Engagement: opens and clicks are imperfect, but “read vs delete” and “move to inbox” are strong signals.

Two realities to internalize:

  1. Receivers trust domains and IPs, not your intentions.
  2. Receivers optimize for user safety, not your conversion funnel.

Interesting facts and short history

  • SMTP predates spam filters: the original mail protocols assumed cooperative users on a friendly network. The internet disagreed.
  • SPF started as a response to forged envelope senders: it checks the domain in the Return-Path (MAIL FROM) by default, not the visible From.
  • DKIM came from domain-signing experiments at Yahoo and others: it was built to survive transit and prove a domain took responsibility for content.
  • DMARC is relatively young: it standardized the “alignment” requirement that SPF and DKIM didn’t enforce on their own.
  • “p=reject” didn’t become mainstream overnight: large brands ramped from none → quarantine → reject over months because misalignment breaks legitimate flows.
  • Mailing lists famously break DKIM: footers and subject tags modify signed content; canonicalization helps, but not always.
  • SPF has a hard DNS lookup limit (10): exceed it and you can get permerror—authentication fails even if your IP is authorized in spirit.
  • ARC exists because forwarding breaks authentication: it’s a chain-of-custody mechanism for “this passed when I received it.”
  • Major providers increasingly require authentication: over time, “no SPF/DKIM/DMARC” moved from “suspicious” to “non-deliverable at scale.”

SPF, DKIM, DMARC: done right (and aligned)

SPF: your allowed senders, with sharp edges

SPF is a DNS TXT record that says which IPs (or which other SPF records) are allowed to send for a domain. Receivers evaluate it against the envelope sender domain (Return-Path / MAIL FROM) or, sometimes, the HELO domain.

Operational guidance:

  • Keep SPF simple. Fewer includes, fewer lookups, fewer surprises.
  • Don’t use “+all”. That’s not “permissive,” it’s “please spoof me.”
  • Use “~all” only during migration. Softfail is a learning wheel; eventually you need “-all”.
  • Own your bounce domain (Return-Path). If you send from a vendor but want DMARC alignment, plan for it.

DKIM: cryptographic accountability, if you don’t break it

DKIM signs parts of the message (headers and body) using a private key. The public key lives in DNS under a selector. Receivers verify the signature. DKIM gives you a stable identity even when IPs change—assuming the signature survives intermediate modifications.

Operational guidance:

  • Sign at the final outbound hop. If an intermediate MTA rewrites content after signing, you’ve just signed a lie.
  • Rotate selectors. Not daily. But treat keys as credentials with a lifecycle.
  • Sign the right headers: From, Date, Subject, Message-ID are common. Avoid over-signing fragile headers that may be rewritten.
  • Use 2048-bit keys where possible. Some receivers treat shorter keys as weaker signals.

DMARC: alignment, policy, and the part you can’t fake

DMARC ties it together. It tells receivers: if SPF and/or DKIM authenticate, do they align with the domain in the visible From header? And what should the receiver do if alignment fails?

Key points teams routinely miss:

  • DMARC is about the visible From: the domain humans see. That’s what phishers forge.
  • Alignment is the whole game: SPF can pass for one domain while From is another. DMARC will still fail.
  • Policy is not enforcement unless you mean it: “p=none” is telemetry, not protection, and providers may discount it as “not serious.”
  • Subdomains matter: decide whether you use organizational domain alignment, and how strict you want it (“adkim=s”, “aspf=s”).

Joke #1: SPF is like a guest list. DKIM is like a wristband. DMARC is the bouncer who checks whether the wristband matches the name on the ticket.

Practical tasks: commands, outputs, and decisions

This section is the part you actually run. Each task includes: a command, what realistic output looks like, what it means, and the decision you make.

Task 1: Pull the SPF TXT record and spot obvious footguns

cr0x@server:~$ dig +short TXT example.com
"v=spf1 ip4:198.51.100.10 include:_spf.vendor-mail.net ~all"

What it means: SPF exists. It authorizes one IPv4 and a vendor include. It ends with softfail (~all).

Decision: If you’re done migrating, move to -all. If you’re still discovering senders, keep ~all briefly, but schedule a cutoff date.

Task 2: Check SPF lookup count (the 10-DNS-lookup trap)

cr0x@server:~$ spfquery -m example.com -ip 198.51.100.10 -sender bounce@example.com -helo mail.example.com
pass (mechanism 'ip4:198.51.100.10' matched)
dns-lookup-count=7

What it means: SPF passes for this IP and stays under the 10 lookup limit.

Decision: If lookup count is near 10, flatten SPF (reduce nested includes). If it’s over 10, fix it before anything else; permerror is a deliverability killer.

Task 3: Verify that your sending IP matches what the receiver sees

cr0x@server:~$ grep -E "postfix/smtp|status=sent" /var/log/mail.log | tail -n 2
Jan 03 10:12:08 mx1 postfix/smtp[21901]: 2F1C81234: to=<user@gmail.com>, relay=gmail-smtp-in.l.google.com[142.250.102.26]:25, delay=1.2, delays=0.1/0/0.4/0.7, dsn=2.0.0, status=sent (250 2.0.0 OK  1704276728 a1e2b3c4d5e6-200si1234567qkb.123 - gsmtp)
Jan 03 10:12:08 mx1 postfix/qmgr[1123]: 2F1C81234: removed

What it means: You delivered to Gmail MX. But this doesn’t prove the source IP used externally (NAT and relays exist).

Decision: Confirm egress IP from the server or via headers at the receiver. If you send through a smart host, SPF must authorize the smart host, not the app server.

Task 4: Confirm egress IP from the sending host

cr0x@server:~$ curl -s https://ifconfig.me
198.51.100.10

What it means: This host’s apparent public IP is 198.51.100.10.

Decision: Ensure this IP is in SPF and has a PTR. If you expected a different IP, you may be on a shared NAT pool—bad for reputation isolation.

Task 5: Check reverse DNS (PTR) for your sending IP

cr0x@server:~$ dig +short -x 198.51.100.10
mail.example.com.

What it means: PTR exists and points to mail.example.com.

Decision: If there’s no PTR, fix that first. If PTR points to an ISP default like static-10-100-51-198.isp.net, consider changing it; mismatched identity is a trust smell.

Task 6: Check forward DNS for the PTR hostname (FCrDNS sanity)

cr0x@server:~$ dig +short A mail.example.com
198.51.100.10

What it means: Forward resolves back to the same IP. Many receivers like this consistency.

Decision: If forward doesn’t match, fix DNS. If you can’t (cloud provider constraints), use their supported rDNS mapping or move outbound mail elsewhere.

Task 7: Validate the DKIM public key exists for a selector

cr0x@server:~$ dig +short TXT s2026._domainkey.example.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtYx...snip...QIDAQAB"

What it means: Selector s2026 is published. Good.

Decision: If it’s missing, DKIM will fail. If you see multiple TXT strings due to DNS splitting, ensure they concatenate correctly (some systems don’t handle it well).

Task 8: Send a test message and inspect Authentication-Results

cr0x@server:~$ swaks --to you@outlook.com --from alerts@example.com --server mail.example.com --tls --header "Subject: auth test" --body "test"
=== Trying mail.example.com:25...
=== Connected to mail.example.com.
=== TLS started with cipher TLS_AES_256_GCM_SHA384
<** snip **>
<-  250 2.0.0 Ok: queued as 1234ABCD

What it means: The message was accepted by your server for outbound delivery. Now you need the received headers at Outlook.

Decision: Pull the message source from the mailbox and read the Authentication-Results. That’s where the real verdict lives.

Task 9: Parse headers locally (quick and dirty)

cr0x@server:~$ sed -n '1,80p' message.eml
Authentication-Results: mx.microsoft.com;
 spf=pass (sender IP is 198.51.100.10) smtp.mailfrom=example.com;
 dkim=pass (signature was verified) header.d=example.com;
 dmarc=pass action=none header.from=example.com
Received: from mail.example.com (mail.example.com [198.51.100.10])
 by NAM11-MW2-obe.outbound.protection.outlook.com with ESMTPS id 123...
From: alerts@example.com
Return-Path: <bounce@example.com>

What it means: SPF, DKIM, DMARC all pass and align (header.from is example.com; smtp.mailfrom is example.com; DKIM d=example.com).

Decision: If you’re still landing in spam with all-pass, stop tweaking DNS and start investigating reputation, content, and engagement.

Task 10: Detect DMARC alignment failure (the common “everything passes but DMARC fails” trap)

cr0x@server:~$ grep -E "Authentication-Results|From:|Return-Path:" -n message.eml | head -n 20
1:Authentication-Results: mx.google.com;
2: spf=pass (google.com: domain of bounce@vendor-mail.net designates 203.0.113.44 as permitted sender) smtp.mailfrom=bounce@vendor-mail.net;
3: dkim=pass header.d=vendor-mail.net;
4: dmarc=fail (p=quarantine sp=quarantine dis=none) header.from=example.com
12:From: Billing <billing@example.com>
13:Return-Path: <bounce@vendor-mail.net>

What it means: SPF and DKIM pass, but for vendor-mail.net. DMARC evaluates alignment against example.com in the visible From. Alignment fails; DMARC fails.

Decision: Either configure the vendor to sign with d=example.com (preferred), or use a vendor feature to use a custom bounce domain aligned to example.com, or change From to a domain you control that aligns with the authenticated identity.

Task 11: Check your DMARC record

cr0x@server:~$ dig +short TXT _dmarc.example.com
"v=DMARC1; p=quarantine; rua=mailto:dmarc-rua@example.com; ruf=mailto:dmarc-ruf@example.com; adkim=s; aspf=s; fo=1; pct=100"

What it means: DMARC exists, quarantine policy, strict alignment for both DKIM and SPF.

Decision: Strict alignment is fine if your systems are disciplined. If you have many third-party senders and you’re still stabilizing, consider relaxing to adkim=r; aspf=r while you fix alignment. Don’t leave it lax forever.

Task 12: Confirm you publish a working MX and you’re not accidentally “parked”

cr0x@server:~$ dig +short MX example.com
10 mx1.example.com.
20 mx2.example.com.

What it means: Domain has MX records; it’s not a dead zone.

Decision: Missing or weird MX can affect perception and bounce handling. Ensure you can receive bounces at minimum.

Task 13: Check your SPF for multiple TXT records (silent breakage)

cr0x@server:~$ dig TXT example.com +noall +answer
example.com. 3600 IN TXT "v=spf1 ip4:198.51.100.10 -all"
example.com. 3600 IN TXT "google-site-verification=abc123"

What it means: This is fine: one SPF record plus an unrelated TXT.

Decision: If you see two SPF records (two TXT strings both starting with v=spf1), SPF evaluation becomes permerror. Merge them. Do not “just add another one.”

Task 14: Confirm Postfix is presenting a sane HELO name

cr0x@server:~$ postconf -n | grep -E "myhostname|smtp_helo_name"
myhostname = mail.example.com
smtp_helo_name = mail.example.com

What it means: HELO is consistent with hostname.

Decision: If HELO is something like localhost or an internal name, fix it. Many receivers score that as “botnet energy.”

Task 15: Make sure you’re not an open relay (yes, still happens)

cr0x@server:~$ swaks --server mail.example.com --to victim@external.com --from attacker@external.com --auth-user bogus --auth-password bogus
=== Trying mail.example.com:25...
<-  220 mail.example.com ESMTP Postfix
-> EHLO test
<-  250-mail.example.com
-> MAIL FROM:<attacker@external.com>
<-  554 5.7.1 <attacker@external.com>: Relay access denied

What it means: Relay denied. Good.

Decision: If this succeeds without auth from the internet, you’re going to be blocklisted before lunch. Fix relay restrictions immediately.

Task 16: Spot greylisting/throttling in logs

cr0x@server:~$ grep -iE "deferred|throttl|try again|4\.7\." /var/log/mail.log | tail -n 5
Jan 03 10:18:22 mx1 postfix/smtp[22555]: 9ABCD1234: to=<user@gmail.com>, relay=gmail-smtp-in.l.google.com[142.250.102.27]:25, delay=120, delays=0.1/0/110/9.9, dsn=4.7.0, status=deferred (host gmail-smtp-in.l.google.com[142.250.102.27] said: 421 4.7.0 Temporary System Problem. Try again later (in reply to end of DATA command))

What it means: Temporary deferral. You may be getting throttled or hitting reputation limits, especially from new IPs/domains.

Decision: Slow down sending, warm up IPs, reduce concurrency, and fix list hygiene. Treat 4xx patterns as “receiver doesn’t trust you yet,” not “network glitch.”

Forwarding, mailing lists, and ARC: where good mail goes to die

Email forwarding is deliverability’s haunted house. A message that passes SPF and DKIM at the original receiver can fail downstream after forwarding because:

  • SPF fails: the forwarder sends from its own IP, which isn’t authorized in the original domain’s SPF.
  • DKIM breaks: the forwarder or mailing list modifies the body or headers (adds footers, changes MIME boundaries, rewrites subject).
  • DMARC fails: alignment can’t be satisfied if both SPF and DKIM no longer validate for the From domain.

ARC (Authenticated Received Chain) exists to add a “chain of custody.” A forwarder can say: “When I got this message, SPF/DKIM/DMARC passed.” Receivers can use that as a trust signal. Not all receivers weigh ARC equally, but it helps in the messy middle where legitimate forwarding is common.

What you should do:

  • If you operate forwarding infrastructure, consider implementing ARC signing.
  • If you rely on mailing lists, be realistic: DMARC policies like p=reject can cause list delivery issues unless the list rewrites From or supports DMARC mitigations.
  • For transactional email, avoid sending through paths that rewrite content after DKIM signing. Sign last.

Reputation: IP, domain, content, and engagement signals

Once authentication is correct and aligned, deliverability becomes a slow grind of trust.

IP reputation: the neighborhood matters

A dedicated IP gives you control over your own reputation. A shared IP means you inherit the behavior of strangers. Sometimes the strangers are fine. Sometimes they are a performance art project about ignoring unsubscribe links.

Practical guidance:

  • Warm up new IPs: start low volume, ramp gradually, and prioritize engaged recipients.
  • Separate traffic types: transactional and marketing should not share identity if you can avoid it. When marketing gets complaints, transactional shouldn’t pay.
  • Be consistent: sudden spikes, new sending hosts, and changing envelope senders look like compromise.

Domain reputation: slow to earn, fast to lose

Domain reputation follows your From domain, your DKIM signing domain, and sometimes your link domains. If you rotate domains to dodge filtering, you’re teaching providers that you behave like spammers. They’re not impressed by your creativity.

Content: not “avoid spam words,” but avoid spam behavior

Filters don’t just hunt for “free money” keywords. They look for patterns correlated with abuse:

  • Link shorteners and redirect chains
  • Display text that doesn’t match the actual link destination
  • Heavily image-based emails with little text
  • Malformed HTML and weird MIME structures
  • Attachments from unknown identities (especially executables, macro docs, or unexpected archives)

Engagement: the part your marketing stack rarely tells you truthfully

“Open rate” is degraded by privacy protections and image proxying. But mailbox providers still have engagement signals you don’t see: read time, reply behavior, moving messages out of spam, and deleting without reading.

Engineering implication: stop sending to people who don’t engage. You’re paying to poison your own reputation.

Joke #2: If you keep emailing people who never open, the mailbox provider assumes you’re either a spammer or an optimist. Neither is a protected class.

Three corporate mini-stories from the trenches

Mini-story 1: The incident caused by a wrong assumption

The company: mid-sized SaaS, steady growth, one outbound mail stream for everything—password resets, invoices, and weekly newsletters. They moved marketing sends to a new vendor to “improve deliverability.” The migration plan was, charitably, a calendar invite.

The wrong assumption was simple: “If SPF and DKIM pass, DMARC will pass too.” The vendor provided an SPF include and DKIM signing for vendor-mail.net. The team kept their visible From as billing@example.com, because brand consistency is sacred.

Within hours, invoices started landing in spam for a subset of customers—mostly enterprise customers with stricter filtering and DMARC enforcement. Support tickets trickled in. Finance escalated. The SRE on-call looked at Postfix logs and saw clean 250s, which is the email equivalent of “works on my machine.”

The breakthrough came from pulling raw headers from a customer mailbox. Authentication-Results showed SPF=pass, DKIM=pass, DMARC=fail. The DMARC policy at example.com was p=quarantine with strict alignment. SPF authenticated vendor-mail.net (Return-Path domain), DKIM authenticated vendor-mail.net (d=), and neither aligned with From example.com.

The fix was not to “relax DMARC because it’s breaking stuff.” The fix was to configure the vendor to use a custom DKIM signing domain aligned to example.com and a custom bounce domain under example.com so SPF alignment could also work. They staged it sender-by-sender, verified alignment in headers, then ramped traffic. Deliverability recovered. Finance stopped pacing.

Mini-story 2: The optimization that backfired

The company: B2C platform with seasonal spikes. They ran their own MTA fleet and wanted to reduce costs. Someone noticed outbound traffic was “bursty” and proposed consolidating to fewer servers with higher throughput, backed by a shiny new NAT gateway. Great: fewer instances, simpler ops, lower bill.

The backfire: IP reputation collapsed. Previously, outbound mail came from several static IPs with stable PTRs. After consolidation, mail egressed from a NAT pool that changed during scaling events. SPF still authorized the vendor relay, DKIM still passed, DMARC still passed. And yet spam placement increased and deferrals spiked.

The team chased content changes, then retried “warming” (without controlling which IP warmed). They added more retries (which increased volume during throttling), and that made deferrals worse. Classic positive feedback loop, except the outcome is “all mail is late and suspicious.”

The resolution required admitting the optimization was the problem. They moved outbound mail back to a small set of dedicated, stable egress IPs with correct PTR and HELO consistency, and they controlled concurrency. Costs went up a bit. Deliverability and latency improved dramatically. The lesson stuck: email is a reputation system; unpredictability is expensive.

Mini-story 3: The boring but correct practice that saved the day

The company: enterprise software vendor. They had multiple teams sending email: product notifications, support desk, and marketing. Years ago, someone with a fondness for “governance” insisted on a central email identity inventory: every sender, every domain, every vendor, every DKIM selector, and the exact SPF mechanisms required. It lived in version control. People grumbled.

Then a vendor breach hit the news cycle and security asked: “Are we using any of these senders?” They were. Worse, that vendor could send with a From that looked like it belonged to the company, because the integration was configured loosely during a rush project.

Because they had the inventory, they quickly identified which streams used the vendor, which domains were affected, and what the authentication posture was. They tightened DMARC on a subdomain used solely for third-party marketing, enforced strict alignment for core transactional mail, and rotated DKIM selectors where appropriate.

Deliverability didn’t take a hit during the change because they rolled it out carefully, checked headers for alignment, and avoided high-risk changes during peak sending. The “boring” practice—documented ownership and controlled DNS changes—meant they could act fast without guessing. Nobody wrote a heroic postmortem. That’s the point.

Common mistakes: symptom → root cause → fix

1) “SPF pass, DKIM pass, still DMARC fail”

Symptom: Authentication-Results shows spf=pass, dkim=pass, but dmarc=fail.

Root cause: Misalignment. SPF authenticated the Return-Path domain (often a vendor domain). DKIM authenticated d=vendor. From is your domain.

Fix: Configure a custom bounce domain under your domain for SPF alignment and/or DKIM signing with d=yourdomain. Verify alignment in headers.

2) “SPF permerror”

Symptom: SPF result is permerror, or receivers treat it as fail.

Root cause: More than 10 DNS lookups due to nested includes, redirects, or macros; or multiple SPF records.

Fix: Flatten SPF. Remove redundant includes. Merge to exactly one SPF record. Keep lookup count comfortably below 10.

3) “DKIM fail only at some providers”

Symptom: DKIM passes at one mailbox provider but fails at another, or passes sometimes.

Root cause: DNS propagation inconsistencies, broken TXT splitting, or intermediate modification after signing (mailing list footers, security gateways, CRM rewrites).

Fix: Ensure DNS returns a stable key everywhere. Sign at the final outbound hop. Avoid rewriting signed parts. Consider relaxed canonicalization, but don’t use it as a bandage for wild rewriting.

4) “Everything passes; still spam”

Symptom: SPF/DKIM/DMARC pass and align, but inbox placement is poor.

Root cause: Reputation or engagement. High complaint rate, sending to stale lists, poor recipient targeting, shared IP issues, or sudden volume spikes.

Fix: Improve list hygiene, reduce volume, warm up properly, segment transactional vs marketing, and stop sending to non-engagers. Fix the business behavior, not the DNS.

5) “Messages disappear, no bounce”

Symptom: Sender logs show accepted delivery, but recipients never see the message (not even spam), and no NDR returns.

Root cause: Silent filtering at the receiver, often due to severe reputation issues or policy blocks; or you’re sending to a sink (typo domain, invalid mailbox) with non-standard behavior.

Fix: Use seeded test accounts across providers to capture headers and placement. Reduce volume, fix authentication/alignment, and address reputation. Ensure bounces are routed and processed correctly.

6) “Forwarded mail fails DMARC”

Symptom: Mail sent to a forwarder (or mailing list) ends up rejected or spammed downstream.

Root cause: SPF breaks on forward, DKIM breaks on modification; DMARC fails.

Fix: For your own forwarding, implement ARC and avoid breaking DKIM. For mailing lists, use DMARC-friendly list behavior (From rewriting or other mitigations) if you control it.

Checklists / step-by-step plan

Checklist A: Stabilize authentication and alignment (the “stop bleeding” plan)

  1. Pick the identity you actually want: decide the canonical From domain(s) for transactional vs marketing.
  2. Inventory all senders: app servers, ticketing systems, CRMs, marketing platforms, monitoring tools.
  3. Ensure one SPF record per domain and keep lookup count below 10.
  4. Ensure DKIM signing for every stream, preferably with d= matching your From domain (or aligned subdomain).
  5. Deploy DMARC with telemetry first: start with p=none only long enough to collect reality.
  6. Fix alignment failures for each sender: custom bounce domains, custom DKIM domains, or adjust From domains.
  7. Move to enforcement: p=quarantine, then p=reject once you’re confident.
  8. Lock down subdomain strategy: separate subdomains for marketing vs transactional, with tailored policies.

Checklist B: Transport and identity hygiene (the “look like a real mail system” plan)

  1. Set stable outbound IPs where possible; avoid NAT pools for primary mail identity.
  2. Configure PTR to match your mail hostname; ensure forward DNS matches the IP.
  3. Set HELO/EHLO to a valid hostname and keep it consistent across MTAs.
  4. Offer STARTTLS correctly; avoid broken TLS configs that cause fallback patterns.
  5. Handle bounces: process DSNs, remove hard bounces, and investigate spikes.
  6. Keep outbound queues healthy; long delays can trigger suspicion and user complaints (“your email arrived two days later”).

Checklist C: Reputation and volume management (the “earn trust” plan)

  1. Segment traffic: transactional on its own identity and IP range if you can.
  2. Warm up: ramp volume gradually; start with your most engaged recipients.
  3. List hygiene: remove hard bounces immediately; sunset non-engagers.
  4. Unsubscribe that works: make it easy; broken unsubscribe is complaint fuel.
  5. Content sanity: consistent templates, minimal link redirects, correct MIME, and avoid sketchy attachment patterns.
  6. Measure placement: seed mailboxes, capture headers, track spam vs inbox across providers.

FAQ

1) If SPF, DKIM, and DMARC are correct, why do I still land in spam?

Because authentication proves identity, not desirability. Spam placement can be driven by reputation (domain/IP), complaint rates, list hygiene, volume spikes, and content patterns.

2) Should I use DMARC p=reject right away?

Not unless you already know all legitimate senders align. Start with p=none briefly to see who’s sending, fix alignment, then move to quarantine and reject.

3) What does DMARC alignment actually mean?

It means the domain in the visible From header matches (or is a subdomain match, depending on strictness) the domain authenticated by SPF (MAIL FROM) and/or DKIM (d=).

4) Is SPF or DKIM more important for DMARC?

DKIM is often more reliable because it survives IP changes and can survive some forwarding. SPF is simpler but breaks on forwarding. In practice, aim for both and ensure at least one aligns.

5) Why does forwarding break SPF?

SPF checks whether the sending IP is authorized for the envelope sender domain. When a forwarder relays mail, the sending IP becomes the forwarder’s IP, which usually isn’t in the original domain’s SPF.

6) What’s the difference between Return-Path and From?

From is what users see and reply to. Return-Path (envelope sender) is where bounces go. SPF authenticates Return-Path by default; DMARC evaluates alignment with From.

7) Can I have multiple SPF records?

No. One SPF record per domain. Multiple v=spf1 TXT records commonly cause permerror, which receivers treat as failure.

8) Do I need a dedicated IP?

If you send meaningful volume and care about reliability, a dedicated IP (or a well-controlled pool) is usually worth it. Shared IPs can be fine, until they aren’t—and you won’t control the “until.”

9) Should marketing and transactional email share the same domain?

You can, but it’s risky. Separate subdomains (or even separate domains) let you protect transactional deliverability from marketing complaints and list decay.

10) What’s the quickest win if we’re suddenly in spam?

Pull headers from affected recipients and confirm alignment. If alignment is fine, reduce volume immediately, stop sending to stale recipients, and investigate deferrals and complaint signals.

Conclusion: next steps that actually move the needle

If your email is landing in spam, treat it like an incident: get evidence from the receiver, identify the failure mode, and fix the shortest path first. Most teams waste days tweaking SPF syntax while DMARC alignment is failing in plain sight.

Practical next steps:

  1. Collect three real message sources from affected inboxes (Gmail, Outlook, and one enterprise domain if possible). Compare Authentication-Results and alignment.
  2. Make alignment explicit: decide whether SPF-aligned bounce domains and/or DKIM-aligned signing domains are your standard for each sender.
  3. Stabilize infrastructure identity: PTR, forward DNS, HELO, and consistent outbound IPs.
  4. Separate risk: split transactional from marketing identities so one bad campaign doesn’t torch password resets.
  5. Hygiene over heroics: stop sending to non-engagers, process bounces, and reduce complaint rate. DNS won’t save a list that hates you.

Email deliverability is boring when it’s healthy, and expensive when it isn’t. Aim for boring.

← Previous
Proxmox “qemu-img: Could not create”: permissions, paths, and filesystem fixes that actually work
Next →
Docker Compose “version is obsolete”: modernize your compose file safely

Leave a comment