Emails From Your App Are Going to Spam: SPF, DKIM, DMARC Fixes
This runbook is for customers whose application emails are landing in spam or promotions instead of the inbox. You will learn how to check the most common causes in order, then apply concrete fixes for SPF, DKIM, DMARC, sending domain alignment, and IP reputation.
TL;DR — If emails from your application are going to spam, the most common cause is missing or broken email authentication: SPF (which servers may send mail for your domain), DKIM (a cryptographic signature added to mail), and DMARC (a policy that tells receivers how to validate alignment). Start by checking your DNS records for the exact domain shown in the email's From address, then confirm your app or email provider is sending from that same domain. Reading time: ~6 min
The scenario
It is Tuesday afternoon and your app is doing what it should: password resets are being sent, contact-form notifications are leaving the system, and the logs say the messages were accepted by your mail provider. But your customer says they never got the reset link, and when they finally check spam, there it is next to fake invoices and crypto scams. You open one of the messages and see "mailed-by" and "signed-by" values that do not match your brand domain. Now you need to work out whether this is a DNS issue, a sender mismatch, or a reputation problem.
Symptoms
- Users report "I only found it in Spam/Junk" or "It went to Promotions, not Primary."
- Gmail shows warnings like:
Be careful with this message
Why is this message in spam? It is similar to messages that were identified as spam in the past.
- Gmail message details show:
SPF: FAIL with IP 203.0.113.10
DKIM: 'FAIL' with domain example.com
DMARC: 'FAIL'
- Outlook or Microsoft 365 may show:
This message looks suspicious
- Your app logs show successful submission, for example:
250 2.0.0 Ok: queued as 12345ABCDE
- Your email provider dashboard shows "delivered" or "accepted," but recipients still report spam placement.
- Mail headers include signals like:
Authentication-Results: mx.google.com;
spf=fail (google.com: domain of no-reply@example.com does not designate 203.0.113.10 as permitted sender) smtp.mailfrom=no-reply@example.com;
dkim=fail header.i=@example.com;
dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=example.com
- You recently changed DNS, switched email providers, or started sending from a new subdomain like
notify.example.com.
Likely causes
| Cause | How common | Quick check |
|---|---|---|
| SPF, DKIM, or DMARC records are missing or wrong | Very common | In your DNS provider dashboard (e.g. Cloudflare: DNS → Records), search for TXT records on your sending domain |
| The app is sending from a different domain than the one you authenticated | Very common | Open a received message → Show original / View source → check From:, Return-Path:, mailed-by, and signed-by |
| Your provider's DKIM is not enabled or not fully verified | Common | In your email provider dashboard, open domain authentication / sender authentication and look for "verified" or "pending" |
| Your sending IP or shared pool has poor reputation | Less common | Send a test to a Gmail address and open Show original; if SPF/DKIM/DMARC all pass but it still lands in spam, suspect reputation/content |
| Message content or sending pattern looks spammy | Less common | Send a plain-text test email with the same From address and subject style removed; if that reaches inbox, content/pattern is involved |
Step-by-step diagnosis
- Check the exact From domain your app is using.
- In your app settings, look for the sender address field, often under a path like
Settings → EmailorNotifications → Sender address. - Or open a received email and inspect the headers.
- In your app settings, look for the sender address field, often under a path like
From: My App <no-reply@example.com>
Return-Path: bounce@mail.example-mail-provider.com
- This is your problem if the visible
From:domain isexample.com, but your DNS authentication was set up only formg.example.comor another domain. - If mismatched, jump to Fixes → The app is sending from a different domain than the one you authenticated.
- Check whether SPF, DKIM, and DMARC exist in DNS for that domain.
- In your DNS provider's dashboard (e.g. Cloudflare:
DNS → Records, Route 53:Hosted zones → your domain), search for TXT and CNAME records. - You are looking for records like:
- In your DNS provider's dashboard (e.g. Cloudflare:
Type: TXT Name: @ Value: v=spf1 include:spf.yourmailprovider.com ~all
Type: TXT Name: _dmarc Value: v=DMARC1; p=none; rua=mailto:dmarc@example.com
Type: CNAME Name: selector1._domainkey Value: selector1-example-com._domainkey.yourmailprovider.com
- This is your problem if SPF is missing,
_dmarcis missing, or the DKIM selector records your provider asked for are absent. - If missing or obviously wrong, jump to Fixes → SPF, DKIM, or DMARC records are missing or wrong.
- Check whether your email provider says the domain is verified.
- In your email provider dashboard, open the section usually called
Domains,Sender authentication, orDomain authentication. - Look for statuses such as:
- In your email provider dashboard, open the section usually called
SPF: verified
DKIM: pending
DMARC: not configured
- This is your problem if DKIM is still pending, failed, or disabled even though you added records.
- If so, jump to Fixes → Your provider's DKIM is not enabled or not fully verified.
- Send a test and inspect authentication results.
- Send one email from the app to a Gmail address you control.
- In Gmail: open the message → three-dot menu →
Show original. - Problem output looks like:
SPF: FAIL
DKIM: FAIL
DMARC: FAIL
- If any of those fail, jump to the matching fix section below. If all pass and mail still lands in spam, continue.
-
Rule out content and sending-pattern issues.
- Send a plain-text test from the same sender with a simple subject like
Your login linkand no attachments, tracking pixels, or shortened links. - This is your problem if the plain-text test reaches inbox but your normal template goes to spam.
- If so, jump to Fixes → Message content or sending pattern looks spammy.
- Send a plain-text test from the same sender with a simple subject like
-
Suspect reputation if authentication passes and content is simple, but spam placement continues.
- Check whether you recently started sending higher volume, switched providers, or moved to a new dedicated IP (a single sending address assigned to you).
- This is your problem if Gmail
Show originalsays SPF, DKIM, and DMARC all pass, but the message still lands in spam consistently across recipients. - Jump to Fixes → Your sending IP or shared pool has poor reputation.
Fixes
SPF, DKIM, or DMARC records are missing or wrong
Add the exact DNS records your email provider gives you for the domain in your From: address.
Typical examples:
TXT @ v=spf1 include:spf.yourmailprovider.com ~all
TXT _dmarc v=DMARC1; p=none; rua=mailto:dmarc@example.com; adkim=s; aspf=s
CNAME selector1._domainkey selector1-example-com._domainkey.yourmailprovider.com
CNAME selector2._domainkey selector2-example-com._domainkey.yourmailprovider.com
If your DNS host asks for the full name, enter _dmarc.example.com. If it asks for only the host part, enter _dmarc.
If you prefer command-line checks and have terminal access:
dig TXT example.com +short
dig TXT _dmarc.example.com +short
dig CNAME selector1._domainkey.example.com +short
Do not create multiple SPF TXT records for the same hostname. If one already exists, merge includes into a single record.
Verify it worked: send a new test email and in Gmail Show original confirm SPF: PASS, DKIM: PASS, and DMARC: PASS.
The app is sending from a different domain than the one you authenticated
Change the app's sender address so the From: domain matches the domain you authenticated in DNS and in your email provider.
Typical app setting:
Settings → Email → Sender address
Change: no-reply@company.com
To: no-reply@mail.company.com
Only do this if mail.company.com is the domain you authenticated with SPF/DKIM/DMARC. If your brand domain is company.com and that is what you authenticated, send from no-reply@company.com instead.
If your app uses environment variables, the setting may look like:
MAIL_FROM_ADDRESS=no-reply@example.com
MAIL_FROM_NAME="Example App"
After changing it, redeploy or restart the app using your hosting dashboard's restart/redeploy button.
Verify it worked: open a fresh email's headers and confirm From:, signed-by, and header.from all use the same domain family.
Your provider's DKIM is not enabled or not fully verified
In your email provider dashboard, open the authenticated domain and click the button usually labeled Verify DNS, Check records, or Enable DKIM.
If the provider gave you CNAME records, copy them exactly into your DNS provider. Common mistakes are:
- entering the target with your domain appended twice
- using TXT instead of CNAME
- adding quotes around a CNAME target
Example of correct records:
CNAME selector1._domainkey.example.com selector1-example-com._domainkey.provider.net
CNAME selector2._domainkey.example.com selector2-example-com._domainkey.provider.net
DNS changes can take time to propagate. If your provider shows pending, wait 15-60 minutes, then click Verify again.
Verify it worked: the provider dashboard shows DKIM as verified or active, and a new test email shows DKIM: PASS.
Your sending IP or shared pool has poor reputation
First, do not switch providers immediately; fix authentication and content first. If those already pass, lower volume spikes and warm up gradually.
Concrete actions:
- Send only transactional mail first (password resets, receipts).
- Pause bulk announcements for 3-7 days.
- Keep daily volume steady instead of jumping from 50 to 5,000 in one day.
- Remove invalid or inactive recipients from your list.
If your provider offers shared vs dedicated IP choices, ask their support which pool your account is on and whether they see reputation issues. If you have a dedicated IP, ask for warm-up guidance before moving all traffic to it.
Verify it worked: over several days, the same authenticated test messages begin landing in inbox more consistently, especially at Gmail and Outlook.
Message content or sending pattern looks spammy
Simplify the template and remove common spam signals.
Change these items first:
- Replace link shorteners like bit.ly with your own full domain links.
- Remove all-caps subjects like "URGENT ACTION REQUIRED!!!"
- Reduce image-only layouts; include real text.
- Add a plain-text part if your provider supports multipart emails.
- Use a real reply-to address monitored by a human.
If your app has a template editor, update the subject and body there. If your agency manages templates in code, ask them to change the HTML and plain-text versions together.
Example plain-text fallback:
Subject: Your password reset link
Hi {{name}},
Use this link to reset your password:
{{reset_link}}
If you did not request this, you can ignore this email.
Verify it worked: a simplified test email from the same sender reaches inbox when the old template did not.
Prevention
- Add a DNS checklist to every new sending domain or subdomain.
Before go-live, confirm:
- SPF TXT exists for the exact From domain
- DKIM records exist and provider says verified
- _dmarc TXT exists
- From address in app matches authenticated domain
- Keep a copy-paste DMARC record in your setup docs.
TXT _dmarc v=DMARC1; p=none; rua=mailto:dmarc@example.com; adkim=s; aspf=s
Start with p=none for reporting, then tighten later with your mail admin if reports look clean.
- Add an automated post-deploy smoke test for email authentication.
After each deploy, send one test email to a mailbox you control and check Gmail → Show original for SPF/DKIM/DMARC PASS.
If your agency has CI/CD, store this as a release checklist item.
- Pin the sender domain in app configuration so it does not drift.
MAIL_FROM_ADDRESS=no-reply@example.com
MAIL_FROM_NAME="Example App"
Review this whenever you change providers or move to a new subdomain.
- Monitor bounce and complaint rates in your email provider dashboard.
Check weekly:
- hard bounces rising
- spam complaints rising
- sudden drops in delivered/opened mail
A sudden jump often appears before inbox placement gets noticeably worse.
- Separate transactional and marketing mail onto different subdomains.
Transactional: no-reply@notify.example.com
Marketing: news@mail.example.com
This limits reputation damage if one stream gets more complaints than the other.
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI