Parse your domain's SPF record, count DNS lookups, expand includes recursively, and flag common misconfigurations that cause email deliverability issues.
Sender Policy Framework (SPF) is a DNS TXT record listing the servers allowed to send email using your domain. When a receiving server gets a message claiming to be from you, it looks up this record and checks whether the sending IP is on the list. If it isn't, the receiver applies whatever policy your record specifies.
Get it wrong in one direction and legitimate mail lands in spam. Get it wrong in the other and anyone can impersonate your domain. Both failures are silent — nothing warns you, the mail just stops arriving or the spoofing just works.
Every SPF record ends with an all mechanism, and the single character in front of it decides what happens to mail that fails the check. This is the most consequential character in the whole record, and the one most often left wrong.
| Qualifier | Name | What receivers do | Use it when |
|---|---|---|---|
| -all | Fail (hard) | Reject the message outright. | You are confident every sending source is listed. This is the goal state. |
| ~all | SoftFail | Accept, but mark as suspicious — usually spam folder. | You are still discovering which services send as you. A staging post, not a destination. |
| ?all | Neutral | Nothing. Treated as if no policy existed. | Almost never. It provides no protection while looking like it does. |
| +all | Pass | Accept everything — every server on the internet passes. | Never. This explicitly authorises the entire internet to send as your domain. |
The practical answer: move to -all. ~all is where most domains stall, because it is safe — nothing breaks when you get the record wrong. But a SoftFail still lets spoofed mail reach the inbox in many configurations, so a domain sitting on ~all indefinitely is carrying most of the risk it thinks it removed.
The sequence that works: publish ~all, turn on DMARC reporting, watch for a few weeks until every legitimate source appears in the reports, then switch to -all.
A domain that never sends email should publish v=spf1 -all. Parked domains and brand-defensive registrations are routinely spoofed precisely because nobody thought they needed a policy.
A record is v=spf1 followed by mechanisms evaluated left to right. The first match wins, which is why all goes last.
| Mechanism | Matches | DNS lookups |
|---|---|---|
| ip4:192.0.2.0/24 | Sender IP in this IPv4 range | 0 |
| ip6:2001:db8::/32 | Sender IP in this IPv6 range | 0 |
| a | The domain's own A/AAAA record | 1 |
| mx | The domain's mail servers | 1 (plus 1 per MX host) |
| include:example.com | Another domain's SPF record, recursively | 1 (plus everything inside it) |
| exists:%{i}.example.com | A macro-expanded hostname resolves | 1 |
| redirect=example.com | Replaces this record entirely | 1 |
| ptr | Reverse DNS of the sender — deprecated | many; do not use |
Two syntax rules break records more often than anything else:
RFC 7208 caps SPF evaluation at 10 DNS lookups. Exceed it and the result is permerror — receivers treat the record as broken, and depending on their configuration your mail may be rejected outright. It fails the same way for everyone, but only some of the time in ways you notice.
This is the single most common way a working SPF record quietly stops working. Each include: costs one lookup, plus every lookup inside the record it pulls in. Adding one more SaaS sender to a record already at nine is enough to break every message you send.
Three ways out, in order of preference:
The checker above counts the full recursive chain and shows where you stand against the limit, including the lookups hidden inside each include.
Enter your domain in the checker above — it resolves the record, expands every include, counts the lookups and flags the problems. If you would rather do it by hand:
Reading the output, check four things in order: that exactly one v=spf1 record exists, that every service you send through is represented, that the chain stays within 10 lookups, and that the record ends in -all rather than +all or ?all.
| Mistake | What happens |
|---|---|
| Two v=spf1 records | Permanent error. The check fails entirely — worse than having no record. |
| Ending in +all | Every server on the internet is authorised to send as you. |
| Over 10 lookups | permerror. Legitimate mail starts failing with no warning. |
| Using ptr | Deprecated, slow, and unreliable. Receivers may ignore it. |
| SPF alone, no DKIM or DMARC | SPF breaks on forwarded mail. Without DKIM, forwarded messages fail. |
| Parked domain with no record | Trivially spoofable. Publish v=spf1 -all. |
SPF authorises servers. It does not survive forwarding, and it says nothing about what receivers should do when a check fails — that is DMARC's job. The three work together:
Checking all three at once, with a scored report: Email Deliverability Audit.