You sent the customer's transactional email, the SMTP log says 250 OK, but the customer says they saw nothing. Or worse: the password reset form doesn't work and the user thinks the site is broken. The problem here isn't sending; the problem is email deliverability. Your server delivered the message, but the recipient's server dropped it into the spam folder or rejected it entirely. Until you accept this distinction, any change in code is useless.
First, figure out where the email died
Before anything else, you need to understand at which stage of the chain the message was lost. There are three cases, and the treatment for each is completely different:
- Rejected (Bounce): The recipient's server responded with a 5xx code. You see this in your own MTA log.
- Accepted but marked as spam: You got a 250 code but the message is in Junk. Here the problem is reputation, not sending.
- Accepted and lost: Rare, but it happens when the recipient has an internal organizational filter.
For the second case, the only sure way to know is to create a real mailbox on Gmail and Outlook and send a test to it. "Spam test" tools that only give a score are misleading; they don't simulate the recipient's actual behavior.
DNS records: where most sites lose
Three records must be correct, and all three must be aligned with a single domain. If one is missing, the others become ineffective too.
SPF
A TXT record on the sending domain. Its simplest correct form is this:
v=spf1 include:_spf.google.com include:spf.servername.ir -all
Important point: -all means any server not in the list is explicitly rejected. Many people use ~all, which only "soft" rejects, and spammers exploit exactly this. If you're sure you know all sending sources, use -all.
Common mistake: two separate SPF records on one domain. The standard says that in this case the result is permerror, and strict recipients reject entirely. Check with dig TXT example.com +short that you see only one v=spf1 record.
DKIM
A cryptographic signature on the message header. You put the public key in DNS and the sending server signs with the private key. Without DKIM, any change along the message path (such as being forwarded) destroys its validity. Set the key length to 2048; 1024 still works but some recipients give a negative score.
DMARC
A record that tells the recipient what to do with messages that fail SPF and DKIM:
v=DMARC1; p=none; rua=mailto:dmarc@example.com; pct=100
Always start with p=none and read the reports for a few weeks. If you set p=reject directly and an old sending source has been left behind, your transactional email will die that very day. After the reports show that all sources are under control, move to quarantine and then reject.
Warming up a new domain: what everyone underestimates
A new domain or new IP has no reputation. If you send 5,000 emails on the first day, even with complete SPF and DKIM, there's a high chance they'll go to spam. Recipients look at the sending pattern, not just the records.
A practical plan: 50 emails on day one, 100 on day two, and increase by about 1.5x each day until you reach your real volume. This takes two to three weeks. If you're in a hurry, use a reputable transactional sending service that has a shared IP with good reputation; it costs more but cuts setup time from three weeks to a few hours.
Here's where people make a mistake: the technical team warms up the domain but at the same time sends bulk marketing email from the same IP. The result is that the IP's reputation burns out in the very first week, and after that no email, not even transactional, gets delivered. Keep transactional and promotional sending on separate domains and IPs. This separation is more important than any other setting.
Feedback loop and complaint reporting
When a user hits "report spam," large recipients notify the sender through an FBL (Feedback Loop). If you haven't signed up, you don't see this signal and only see that your delivery rate has dropped.
For Gmail use Postmaster Tools, and for Outlook use SNDS. The complaint threshold is below 0.1%; above 0.3% you usually run into serious trouble. If the complaint rate goes up, the first thing to do is clean the list, not change the email copy.
The rua record in DMARC also sends aggregate reports. These reports are XML and unreadable raw; use a parser tool to turn them into a table and look every week at which IP is sending email on behalf of your domain. I've seen many times that a forgotten old server is still sending email on behalf of the domain and dragging down the whole domain's reputation.
Reading the header of a rejected email
When an email goes to spam, the full message header tells you everything. In Gmail, on the message, click the three-dot menu and then "Show original." Look for these lines:
Authentication-Results: shows the result of SPF, DKIM, and DMARC. If you seespf=fail, the problem is DNS.Received: the message path. If the number of hops is unusually high, there's an unknown relay in the path.X-Spam-ScoreorX-Spam-Status: if the recipient has SpamAssassin, it writes the score and the reason.
A real example of a problematic header:
Authentication-Results: mx.google.com;
spf=pass smtp.mailfrom=example.com;
dkim=fail header.d=example.com;
dmarc=fail (p=NONE sp=NONE) header.from=example.com
Here SPF passed but DKIM failed. That means the DNS record is correct but the private key on the server doesn't match the public key in DNS. This usually happens after a server migration or key regeneration, and nobody remembers to update the record. You can test this directly with opendkim-testkey -d example.com -s selector -vvv.
Tools and periodic checks
You can do a complete audit in half an hour if you know where to look. To check DNS records and resolution, use the DNS and network lookup tools and compare the result with your own DMARC report. If your sending server is on your own infrastructure, make sure the IP isn't on any public blocklist; blacklist check tools tell you this in a few seconds.
A point that gets less attention: if your site comes under a DDoS attack and the email sending server is on the same infrastructure, the performance drop can cause delivery timeouts and messages queuing up. In such conditions, DDoS protection, apart from the security discussion, also helps email delivery stability.
For documenting audit results and keeping a history of record changes, a simple notebook is enough, but if the team is large it's better to do this within the framework of audit logs so it's clear who changed the DKIM record and when.
Frequently asked questions
Why do my site's transactional emails go to spam even though SPF and DKIM are correct?
Correct records are a necessary condition, not a sufficient one. There are three other common reasons: low IP or new domain reputation, high user complaint rate, and email content that has spam patterns (shortened links, large image with no text, all-caps subject). First open the header of a real email in Gmail and read Authentication-Results; if all three passed, the problem is reputation, not settings.
How long does email domain warm-up take?
For a completely new domain with medium sending volume, two to three weeks are needed to reach the final volume. If your sending volume is high or you have urgency, using a sending service with a shared IP and established reputation is faster, but you'll have less control over the IP's reputation.
What's the difference between SPF, DKIM, and DMARC in one sentence?
SPF says which servers are allowed to send on behalf of your domain, DKIM proves the message wasn't tampered with along the path, and DMARC determines what the recipient should do with a message that failed those two. All three must be configured on one domain to have an effect.
Should I send transactional and newsletter email from the same domain?
No. This is the most common mistake I see in email deliverability audits. If a newsletter campaign gets a high complaint rate, the reputation of that same domain is damaged and the password reset email goes to spam too. Keep the main domain for transactional and send the newsletter from a subdomain with a separate IP.
The next step is clear: send a test email to a Gmail mailbox, open its full header, and read the Authentication-Results line. If all three passed, go to the DMARC report and complaint rate; if one failed, fix that one first. The rest of the work only makes sense after this stage.
Comments 0
No comments yet — be the first!