DEV Community

Regő Botond Ronyecz
Regő Botond Ronyecz

Posted on • Edited on • Originally published at zerohook.hashnode.dev

How to Test Your SPF Record for Common Mistakes (Step by Step)

Your SPF record looks correct. You added the include values your email provider told you to add. You saved the record. Emails are still landing in spam.

The problem is almost certainly one of five things — and four of them are invisible in your DNS provider's interface. You cannot tell by looking at the record whether you have exceeded the 10-lookup limit. You cannot tell from a browser whether your propagation has completed. You cannot see from the dashboard that a second SPF record was created instead of the existing one being edited.

This guide covers how to test for each of the five most common SPF mistakes, what the symptoms of each one look like, and the exact fix.


The Basics: What SPF Is and What It Isn't

SPF (Sender Policy Framework) is a TXT record published at your root domain that lists which mail servers are authorized to send email on your behalf. When a receiving server gets an email claiming to be from your domain, it checks your SPF record and asks: did the sending server have permission?

The record structure:

v=spf1 include:_spf.google.com include:sendgrid.net ~all
Enter fullscreen mode Exit fullscreen mode
  • v=spf1 — required, always first, identifies the record as SPF
  • include: — delegates authorization to another domain's SPF record
  • ~all — soft fail: unauthorized senders are flagged but still delivered
  • -all — hard fail: unauthorized senders are rejected entirely
  • +all — allows everyone: never use this

SPF does not encrypt email. It does not stop spam. It tells receiving servers who is allowed to send email from your domain — and when combined with DKIM and DMARC, it provides the full authentication chain that inbox providers use to decide where your email lands.


Step 1: Check That Your SPF Record Exists

Before testing for mistakes, confirm the record is actually there.

dig yourdomain.com TXT +short | grep spf
Enter fullscreen mode Exit fullscreen mode

Expected output:

"v=spf1 include:_spf.google.com ~all"
Enter fullscreen mode Exit fullscreen mode

If you get no output: Your domain has no SPF record. Create one — the absence of SPF means any server on the internet can send email claiming to be from your domain, and receiving servers have no way to verify it is fake.

If you get output but it does not start with v=spf1: You have a TXT record but it is not a valid SPF record. Check for typos — a common one is v=spfl (lowercase L instead of the digit 1).


Mistake 1: Two SPF Records on the Same Domain

This is the most common SPF mistake and the most damaging. It happens when someone creates a new SPF TXT record instead of editing the existing one — a natural thing to do if you are unfamiliar with DNS and want to add a new sending service.

The rule: Only one TXT record starting with v=spf1 is allowed per domain. Two records instantly invalidates both. Every email you send fails SPF. The symptom — emails landing in spam or being rejected — appears immediately after the second record propagates.

How to test:

dig yourdomain.com TXT +short
Enter fullscreen mode Exit fullscreen mode

Count the lines in the output that start with v=spf1. If you see two, you have a duplicate.

"v=spf1 include:_spf.google.com ~all"
"v=spf1 include:sendgrid.net ~all"
Enter fullscreen mode Exit fullscreen mode

That is two SPF records. Both are now invalid.

The fix: Go to your DNS provider. Delete one of the records. Edit the remaining one to include all the values from both:

v=spf1 include:_spf.google.com include:sendgrid.net ~all
Enter fullscreen mode Exit fullscreen mode

Provider note: DigitalOcean's DNS panel does not have an inline edit button for existing records — you delete and recreate. Cloudflare and Namecheap have pencil icons to edit in place.


Mistake 2: Exceeding the 10 DNS Lookup Limit (SPF PermError)

SPF allows a maximum of 10 DNS lookups during validation. Each include: directive in your record counts as one lookup. Each of those included records may itself contain include: directives, which count as additional lookups — recursively.

When the 10-lookup limit is exceeded, SPF validation returns a PermError result. Many receiving servers treat PermError the same as a hard fail — your email is rejected or sent to spam, and the SPF result in the email headers shows permerror or fail.

The problem is invisible. Your record may look fine with only three include: values. But if one of those included records itself includes four more records, you may already be at seven lookups before counting the rest.

How to test:

The command-line approach requires manually counting recursive lookups, which is tedious. Use an online SPF checker instead — MXToolbox's SPF lookup at mxtoolbox.com/spf.aspx shows the total lookup count including recursive includes:

# Quick local check: count your direct includes
dig yourdomain.com TXT +short | grep -o 'include:[^ ]*' | wc -l
Enter fullscreen mode Exit fullscreen mode

This counts only your direct includes, not recursive ones — use it as a first indicator. If you have 6+ direct includes, you are likely over the limit when recursive lookups are counted.

Common high-lookup culprits:

Service Include value Recursive lookups
Google Workspace include:_spf.google.com 1
Microsoft 365 include:spf.protection.outlook.com 1
SendGrid include:sendgrid.net 1
Salesforce include:_spf.salesforce.com 3–4
Mailchimp / Mandrill include:servers.mcsv.net 2–3
HubSpot include:_spf.hubspot.com 2
Zendesk include:mail.zendesk.com 2

A setup with Google Workspace, SendGrid, Salesforce, HubSpot, and Zendesk is likely already over the limit.

The fix: Remove include: entries for services you no longer use. If you legitimately use more services than the limit allows, use SPF flattening — a tool that resolves all the include chains into their constituent IP addresses and writes a single record with explicit ip4: and ip6: entries instead of include: directives. autospf.com and dmarcian's SPF Surveyor both do this. ZeroHook flags the PermError in its SPF audit check and shows the current lookup count alongside the fix, so you can see exactly how much headroom you have left. The tradeoff is that flattened records need to be updated whenever your email providers change their sending IPs, which happens periodically without announcement.


Mistake 3: Not Including All Your Sending Services

SPF only authorizes the services explicitly listed in your record. Every service that sends email as your domain — your inbox provider, your transactional email platform, your CRM, your marketing tool, your HR system — needs to be in your SPF record or its emails will fail authentication.

The symptom is partial: some emails pass SPF and some fail, which is harder to diagnose than a complete failure. DMARC reports (if you have DMARC configured with a rua= address) will show exactly which sources are failing — the unauthorized senders appear in the reports with spf=fail.

How to find your sending services:

Every service that has ever sent email from your domain appears in your DMARC aggregate reports. If you do not have DMARC configured yet, check your email provider's admin panel for a list of allowed senders, and audit each tool your company uses that sends any automated email: invoices, support tickets, password resets, marketing campaigns.

The standard include values for common providers:

Google Workspace:    include:_spf.google.com
Microsoft 365:       include:spf.protection.outlook.com
Zoho Mail (global):  include:zoho.com
Zoho Mail (EU):      include:zoho.eu
Zoho Mail (India):   include:zoho.in
Fastmail:            include:spf.messagingengine.com
Proton Mail:         include:_spf.protonmail.ch
Apple iCloud:        include:icloud.com
SendGrid:            include:sendgrid.net
Amazon SES:          include:amazonses.com
Mailchimp/Mandrill:  include:servers.mcsv.net
Postmark:            include:spf.mtasv.net
Enter fullscreen mode Exit fullscreen mode

Verify your specific include value with your provider — some (Google Workspace, Microsoft 365, Zoho) show the exact expected SPF record in their admin panel.

How to test:

After adding a new include, send a test email from that service to a Gmail account you control. In Gmail, click the three-dot menu on the email → Show original. Look for the SPF result:

Enter fullscreen mode Exit fullscreen mode

pass means the sending service is in your SPF record. fail or softfail means it is not.


Mistake 4: Using Hard Fail (-all) Too Early

The ~all (soft fail) at the end of your SPF record flags unauthorized senders as suspicious but still delivers their email. The -all (hard fail) rejects unauthorized senders entirely — their emails are not delivered at all.

Hard fail is the correct long-term configuration. Moving to it before you have confirmed that every service sending email from your domain is listed in your SPF record causes those services' emails to be rejected. If you discover a missing service after switching to -all, every email from that service has already been bouncing.

How to test before switching:

dig yourdomain.com TXT +short | grep spf
Enter fullscreen mode Exit fullscreen mode

Check whether your record ends with ~all or -all. If you are at -all and experiencing email delivery issues from specific services, the first step is switching back to ~all immediately:

  1. Find the SPF TXT record in your DNS provider
  2. Edit it — change -all to ~all
  3. Save — propagation takes 5–15 minutes
  4. Use DMARC reports to identify all failing senders
  5. Add missing services to the record
  6. Verify each service passes SPF by checking email headers
  7. Switch back to -all only when all services show spf=pass

The recommendation: Start with ~all. Run in soft-fail mode for at least two to four weeks while reviewing DMARC reports. Switch to -all only after you are confident the record is complete.

Never use +all. It authorizes every server in the world to send email as your domain and completely defeats the purpose of SPF.


Mistake 5: Not Waiting for Propagation Before Testing

DNS changes do not take effect instantly. Your DNS provider saves the record immediately, but cached versions of your old record persist at resolvers around the world until the TTL expires. If your SPF record's TTL is set to 3600 (one hour), changes may take up to an hour to reach all resolvers.

The symptom: you make a change, test immediately, and see the old result. You assume the change did not save. You make the change again. You now have a duplicate record (Mistake 1).

How to test propagation:

# Check what your specific nameserver is serving right now:
dig yourdomain.com TXT +short @ns1.yourdomain.com

# Check what Google's resolver sees (may still be cached):
dig yourdomain.com TXT +short @8.8.8.8

# Check what Cloudflare's resolver sees:
dig yourdomain.com TXT +short @1.1.1.1
Enter fullscreen mode Exit fullscreen mode

If your nameserver shows the new record but @8.8.8.8 shows the old one, propagation is still in progress. Wait and test again.

Typical propagation times by provider:

DNS Provider Typical propagation
Cloudflare 5–15 minutes
GoDaddy 15–30 minutes
Namecheap 15–30 minutes
AWS Route53 5–15 minutes
DigitalOcean 15–30 minutes
Hostinger 15–60 minutes

These are typical times — propagation can take longer if a resolver has cached your record with a high TTL. Check your current TTL:

dig yourdomain.com TXT
Enter fullscreen mode Exit fullscreen mode

Look for the TTL value in the answer section (the number between the domain name and the record type). If it shows 3600, resolvers will cache the old record for up to an hour.


The Full SPF Verification Sequence

Run through this sequence in order after making any SPF change:

Step 1: Confirm only one SPF record exists

dig yourdomain.com TXT +short | grep -c "v=spf1"
Enter fullscreen mode Exit fullscreen mode

Output must be 1. If it is 2 or higher, delete the duplicate before continuing.

Step 2: Check lookup count

Run your record through mxtoolbox.com/spf.aspx and confirm total lookup count is 10 or under.

Step 3: Confirm propagation to major resolvers

dig yourdomain.com TXT +short @8.8.8.8 | grep spf
dig yourdomain.com TXT +short @1.1.1.1 | grep spf
Enter fullscreen mode Exit fullscreen mode

Both should return the same record.

Step 4: Send a test email and check headers

Send an email from your mail client (or from a service you just added to SPF) to a Gmail address you control. In Gmail, click the three-dot menu → Show original. Look for:

Enter fullscreen mode Exit fullscreen mode

or

Enter fullscreen mode Exit fullscreen mode

spf=pass — authorized, SPF configured correctly for this sender.
spf=softfail — sender is not in your SPF record, but you are using ~all.
spf=fail — sender is not in your SPF record and you are using -all.
spf=none — no SPF record found (propagation not complete, or record missing).
spf=permerror — the record exceeds the 10-lookup limit or has a syntax error.

Step 5: Check DMARC reports

If you have DMARC configured with a rua= address, check the reports after 24 hours. Every sender that appears with spf=fail in the aggregate reports is a service you are missing from your SPF record.


Quick Reference: SPF Include Values by Provider

Email Provider Include value to add to SPF
Google Workspace include:_spf.google.com
Microsoft 365 / Outlook include:spf.protection.outlook.com
Zoho Mail (global) include:zoho.com
Zoho Mail (EU datacenter) include:zoho.eu
Zoho Mail (India datacenter) include:zoho.in
Fastmail include:spf.messagingengine.com
Proton Mail include:_spf.protonmail.ch
Apple iCloud include:icloud.com
SendGrid include:sendgrid.net
Amazon SES include:amazonses.com
Postmark include:spf.mtasv.net

Assemble your record by combining the relevant values:

v=spf1 include:_spf.google.com include:sendgrid.net ~all
Enter fullscreen mode Exit fullscreen mode

One record. All services listed. Under 10 total lookups. Ending in ~all to start, -all after verification.


Provider-Specific Notes

Cloudflare: Never set the proxy toggle (orange cloud) on TXT records. SPF records must always be DNS only (grey cloud). The orange cloud is only for A and AAAA records that you want proxied through Cloudflare's CDN.

AWS Route53: TXT record values must be wrapped in double quotes in the Value field. Enter "v=spf1 include:_spf.google.com ~all" with the quotes — Route53 requires them. Other providers do not.

GoDaddy: The Host field for root domain records should be @. GoDaddy adds your domain name automatically — do not type the full domain name in the Host field or the record will be created at yourdomain.com.yourdomain.com.

DigitalOcean: Does not have an inline edit button for DNS records. To update an SPF record, delete the existing one and create a new one with the updated value. Do not create a second record before deleting the first.

Namecheap: Uses a green checkmark button (not a Save button) to confirm record changes. Clicking away without clicking the checkmark discards the change without warning.


TL;DR

  • Only one SPF record is allowed per domain — creating a second one instead of editing the existing one instantly breaks all email delivery. Check with dig yourdomain.com TXT +short | grep -c "v=spf1" — the answer must be 1
  • The 10-lookup limit is recursive and invisible — use an online SPF checker to count total lookups including nested includes before assuming your record is under the limit
  • Start with ~all (soft fail), not -all (hard fail) — switch to hard fail only after DMARC reports confirm every sending service passes SPF
  • Wait for propagation before testing — changes take 5–60 minutes to reach external resolvers depending on your provider; check with dig @8.8.8.8 to see what Google's resolver sees
  • spf=pass in Gmail's Show Original is the ground truth — that is the only check that confirms SPF is working end to end for a specific sending service
  • DMARC reports show every sender failing SPF — if you have rua= configured, the aggregate reports identify every unauthorized sender and every legitimate sender that is missing from your record
  • Run the full audit on your domain — ZeroHook checks SPF syntax, lookup count, duplicate records, and missing senders in one pass, with provider-specific fix steps included: zerohook.org

*Part of an ongoing series on email deliverability and DNS security.

Top comments (0)