Defaults.Exposed

SPF: what your record says, how it fails, and how to fix it

An SPF record is one line of text in your domain’s DNS that names the servers allowed to send email with your domain after the @. Every receiving mail server looks that line up for every message you send. It checks whether the connecting server is on the list and writes the verdict into the message headers. If your mail keeps landing in junk and nobody can tell you why, that verdict is the first thing to read, because it is the one part of the story the receiver spells out in plain text.

You do not need to find the record yourself. The scan below reads your domain’s DNS the way a receiver does, prints the exact SPF record it found, counts the DNS lookups inside it, and tells you how the record ends. The ending is the part that tends to go unread.

What the record is made of

A small business with a hosted mailbox and one marketing tool ends up with a record like this:

v=spf1 include:_spf.mailhost.example include:mail.crm.example ip4:203.0.113.25 -all

It lives as a TXT record on the bare domain (the DNS name with nothing in front of it, shown as @ in most control panels). v=spf1 says which version this is; there has only ever been one. Everything after it is a list of mechanisms, read left to right, and the first one that matches the sending server decides the result.

include:_spf.mailhost.example pulls in another domain’s SPF record and treats its servers as yours. This is how you authorise a provider without knowing its IP addresses. ip4:203.0.113.25 names one address directly; ip4:203.0.113.0/24 would name a block of them. a and mx mean “whatever my A record or MX records point at”. They are cheap to write and expensive to evaluate, because each one costs the receiver a DNS lookup.

-all at the end is the terminal qualifier. It says what to do with a server that matched nothing before it. -all means fail. ~all means softfail, a weaker verdict: the SPF standard (RFC 7208) tells receivers to accept the message and mark it. ?all means neutral, which is the same as saying nothing. +all means every server on the internet passes, so the record no longer excludes anyone and receivers give it no weight.

A receiver will follow at most ten DNS lookups while evaluating the record. include, a, mx, ptr, redirect and exists each cost one, and includes inside includes count against the same ten. Past that, the receiver stops and returns permerror, which is worse than having no record at all, because the receiver treats it as a broken policy.

The census view of the live internet

You are far from alone in having a record and still getting junked. The September 2026 census ran the SPF check on 316,600,902 domains across the whole registered internet. Of those, 84,153,064 publish a record ending in ~all (softfail) and 57,704,425 publish one ending in -all (hard fail).

More of the domains that publish a record chose the ending that tells receivers “mark it, do not refuse it” than chose the strict one, often because a provider’s setup guide said so years ago and nobody revisited it. A softfail record still works, and the consequence is the one you asked for: a message from an unlisted server arrives with a mark against it instead of being refused, and the mark counts towards the junk decision.

How a receiver reads it

SPF checks the envelope sender: the address in the SMTP MAIL FROM command, which shows up in delivered mail as the Return-Path header. It also checks the name the sending server announced in its HELO greeting. The From line your customer sees in their inbox is a different field, and SPF never reads it.

Bulk senders and helpdesk tools often put their own domain in the envelope and your domain in From, so a message can pass SPF on their domain while doing nothing for yours. That gap is why SPF on its own settles nothing about junk placement. DMARC is where the two addresses get compared, and the DMARC piece covers that comparison.

After the check, the receiver stamps the result into the message. You will see one of these two lines, sometimes both:

Received-SPF: softfail (mx.receiver.example: domain of transitioning [email protected] does not designate 198.51.100.7 as permitted sender) client-ip=198.51.100.7;
Authentication-Results: mx.receiver.example; spf=fail (sender IP is 198.51.100.7) smtp.mailfrom=yourshop.example

The results a receiver can write are pass, fail, softfail, neutral, none, permerror and temperror. Only pass helps you. none means no record was found. With permerror the record exists and the receiver could not make sense of it. temperror says DNS did not answer in time, so your nameservers are the problem and the record is fine.

Where to find your own verdict

Send a message from your normal mail client to a mailbox you control at a different provider, then open the raw message. Webmail clients call this “Show original”, “View source”, “View message details” or just “Headers”. Search the text for spf=. Whatever follows is the verdict that receiver reached, and the bracketed text after it is the receiver’s own explanation, usually with the IP address it saw.

If the address in that line is one you do not recognise, that is the whole diagnosis: a service is sending as you, and your record does not list it. Ask that tool’s support page which include: it wants. Every legitimate mail service publishes one.

To read the record as it stands, from any terminal:

nslookup -type=txt yourshop.example

or, on a Mac or Linux machine:

dig +short TXT yourshop.example

You should see exactly one line beginning v=spf1. Two such lines is a fault in its own right, covered below.

Match the line you found

The headings below are the verdicts as receivers write them, so search your headers for the one you have.

spf=none

There is no SPF record on the domain, or it sits on the wrong name (putting it on www or on mail is a common slip). Publish a TXT record on the bare domain. Start with your mailbox provider’s include and the servers you know about. End it with ~all for the first week while you watch the headers, then move to -all once nothing you care about is failing.

spf=fail or spf=softfail on mail you sent yourself

Your record exists, and the server that carried the message is missing from it. Read the IP in the Received-SPF line and work out which service owns it; the tool that sent the message will tell you. Add that service’s include:, or the address itself as ip4:. Publish it and wait for the old TTL to expire. Then send again.

spf=permerror with “too many DNS lookups”

The line in the header reads:

spf=permerror (SPF PermError: too many DNS lookups)

Your record, once every include is unpacked, asks the receiver for more than ten lookups. Remove any include: for a service you no longer use. Replace a and mx mechanisms with the literal ip4: addresses they resolve to, since a literal address costs nothing. If you still cannot get under ten, the record is telling you that too many outside services send as this domain, and some of them should move to a subdomain with an SPF record of its own.

spf=permerror with “multiple SPF records”

Two TXT records on the same name both start with v=spf1. Receivers must treat this as an error, so every message fails, even from servers that both records list. This happens when a second tool’s setup wizard adds its own record instead of editing the one already there. Merge the mechanisms into a single record, delete the other, and check with nslookup that only one line remains.

spf=pass and the mail is still junk

SPF has passed, so the problem sits one layer up. Check that the domain in Return-Path matches the domain in From. If a sending tool uses its own bounce domain, SPF passes for the tool and does nothing for you, and only a DKIM signature in your domain rescues the message. The DKIM piece explains how to find out whether your provider is signing. If both pass and mail is still junked, authentication is fine and the cause is reputation or content, which no DNS change will fix.

Forwarded mail fails, everything else passes

When a recipient forwards your message to another mailbox, the forwarding server becomes the sender, and it is nowhere in your record. SPF cannot fix this by design. The forwarding side should rewrite the envelope (a technique called SRS), and not every forwarder does. Your defence is DKIM, which survives forwarding because the signature travels inside the message.

The record ends in +all or ?all

The record authorises everyone, or nobody in particular. Receivers know this pattern and give it no credit. Replace the ending with ~all today and plan the move to -all. The qualifier piece walks through the difference and the order to do it in.

Publish the change and confirm it

In your DNS control panel, edit the existing TXT record on the bare domain; do not add a second one. Set the TTL to 300 seconds while you are making changes, so mistakes expire fast, and put it back to an hour or more once the record has been stable for a week.

Then send a test to an outside mailbox and read spf= in the headers again. Rerun the scan above and it will show the new record as receivers now see it, with the lookup count and the ending. Once the verdict reads pass from every service you use, the SPF half of your junk problem is closed, and the rest of the fix lives on the DMARC and DKIM pages.

Figures as of 5 September 2026, from the September 2026 edition of the defaults.exposed census. Census numbers move every month; the current values are on the census data page.