Check your SPF record
Counts every DNS lookup and follows every include.
The lookups go from your browser to a public DNS-over-HTTPS resolver. Nothing is sent to Unitpost, and there's no sign-up.
SPF is a TXT record starting with v=spf1 that lists the servers allowed to send mail for a domain's Return-Path. While evaluating it, receivers allow at most 10 DNS lookups (include, a, mx, ptr, exists and redirect, nested includes counted) and 2 void lookups; past either limit SPF returns a permerror for every message. This checker follows every include, counts both, and flags syntax errors, duplicate records and risky all terms.
How SPF lookups are counted
Every term that makes the receiver query DNS counts toward a limit of 10 per evaluation (RFC 7208 §4.6.4), and the count includes every term inside nested includes. Terms that carry their answer in the record cost nothing.
| Term | Lookups | Why |
|---|---|---|
include: | 1, plus every lookup in the included record | The receiver fetches and evaluates the other record. |
redirect= | 1, plus every lookup in the target record | The other record replaces this one. |
a, a:host | 1 | An address lookup for the host. |
mx, mx:host | 1 | An MX lookup. More than 10 MX hosts behind one mx is a permerror on its own. |
ptr | 1 | A reverse lookup of the sending IP. RFC 7208 says not to publish it. |
exists: | 1 | An address lookup, usually on a macro. |
ip4:, ip6: | 0 | The addresses are in the record. |
all | 0 | Matches every sender; nothing to look up. |
exp= | 0 | Fetched only after a fail, to explain it. |
An include can cost several lookups when the record it names has includes of its own. The include tree in the result shows the count for each record, so you can see which one is expensive.
Void lookups
A void lookup is a DNS query that comes back with nothing: the name doesn't exist (NXDOMAIN), or it exists with no record of the type asked for. RFC 7208 has receivers stop after two, and a third makes the result a permerror.
They come from includes of domains that no longer exist (a provider you left, a typo) and from a: or mx: terms naming hosts that were deleted. The fix is to remove the term; it authorizes nothing.
Flattening an SPF record, and what to do instead
Flattening replaces includes with the ip4: and ip6: ranges they currently resolve to. It brings the lookup count down, but it freezes a list the provider can change at any time.
- When a provider adds a range, mail from it fails SPF until you update your record.
- The record grows. RFC 7208 asks for a record whose DNS answer fits in 512 bytes, and one TXT string holds at most 255 characters, so a long record has to be split into several strings.
- Services that re-flatten automatically fix the staleness, but your SPF then depends on them publishing correctly.
Two changes usually bring the count down without flattening:
- Remove includes for services you no longer send through, and any include that appears twice.
- Give senders their own Return-Path subdomain. SPF is checked on the Return-Path domain, and each name has its own budget of 10 lookups. Providers that use a custom return-path subdomain publish SPF there, not in your apex record. Unitpost uses
unitpost.<your domain>, so it adds nothing to your apex record.
~all or -all: softfail vs fail with DMARC
With DMARC enforced, the choice matters less than it seems: DMARC acts on SPF and DKIM together, and a message that fails SPF but passes aligned DKIM still passes DMARC.
| Ending | Result for unlisted servers | Use it when |
|---|---|---|
-all | fail | The domain sends no mail (v=spf1 -all), or every sender is listed and you accept the forwarding risk below. |
~all | softfail | The default for a domain with DMARC at quarantine or reject. |
?all | neutral | Never on a sending domain; it asserts nothing. |
+all or all | pass | Never; it authorizes every server on the internet. |
The catch with -all: some receivers reject on an SPF fail before they evaluate DKIM or DMARC (RFC 7489 §10.1). Forwarded mail always fails SPF, so -all can lose forwarded messages that DKIM would have carried. ~all with an enforced DMARC policy still stops spoofing.
Errors and how to fix them
No SPF record found
Also reported as: SPF record not found; No SPF record; spf=none
No TXT record at this name starts with v=spf1, so receivers get an SPF result of none and can't tell which servers may send for it.
- Check the right name. SPF is looked up on the Return-Path domain, which is often a provider's subdomain of yours; for Unitpost it is
unitpost.<your domain>. - If this name sends mail, publish one TXT record at it:
v=spf1 include:<your provider's include> ~all, using the include each provider documents. - If it sends no mail, publish
v=spf1 -allso receivers can reject anything that claims to be from it. - Wait for the TTL and check again. Other causes are in SPF record not found.
Multiple SPF records found
Also reported as: More than one SPF record; SPF permerror: multiple records; Found multiple SPF records
RFC 7208 allows one v=spf1 record per name. With two or more, receivers return a permerror and SPF fails for every message, not just mail from one of the providers.
- List what DNS serves:
dig TXT example.com +short. - Merge the terms into one record with a single
v=spf1at the start and a singleallat the end:v=spf1 include:_spf.one.example include:_spf.two.example ~all. - Delete the other records and check again.
SPF syntax error
Also reported as: Invalid SPF record; SPF permerror: unknown mechanism; SPF record is not valid
A term isn't valid SPF: an unknown mechanism, = where a mechanism takes :, a host name in ip4:, an invalid address, or a second redirect=. Receivers return a permerror for the whole record, so SPF fails for every message.
- Read the result line: it quotes the term and says what's wrong with it.
- Correct the term.
include=_spf.example.combecomesinclude:_spf.example.com;ip4:mail.example.combecomesa:mail.example.com;ip4:203.0.113.300needs a real address such asip4:203.0.113.30. - Separate terms with single spaces and check again after the TTL.
SPF permerror: too many DNS lookups
Also reported as: Too many included lookups; SPF record exceeds 10 DNS lookups; PermError: too many DNS lookups
Evaluating the record takes more than 10 DNS lookups once nested includes are counted. Receivers stop at 10 and return a permerror once a message's check reaches the 11th, so mail from senders listed late, or not listed at all, fails SPF. The same finding covers an mx term whose domain has more than 10 MX hosts, which RFC 7208 also makes a permerror.
- Find the expensive records in the include tree; each shows its own lookup count.
- Remove includes for services you no longer send through, and any include that appears twice.
- Replace
aandmxterms with theip4:andip6:addresses they point at, when those are stable addresses you control. - Move bulk or marketing senders to their own Return-Path subdomain, which has its own budget of 10.
- Flatten provider includes into IP ranges only as a last resort; see flattening.
SPF record is close to the 10-lookup limit
Also reported as: 9 of 10 SPF lookups used; SPF lookup count near limit
The record uses 9 or 10 of the 10 lookups receivers allow. It passes today, but one more include, or a provider adding a nested include to its own record, tips it into a permerror that fails every message.
- Remove includes for services you no longer use.
- Move a sender to its own Return-Path subdomain before you add the next provider.
- Check again after any provider tells you to change your SPF record.
SPF void lookup limit exceeded
Also reported as: Too many void DNS lookups; SPF permerror: void lookup limit; More than 2 void lookups
A void lookup returns nothing: the name doesn't exist, or has no record of the type asked for. Receivers allow 2 per evaluation and return a permerror on the third. One or two show as a warning, because each is a term that authorizes nothing.
- Find the terms that point nowhere: includes of domains that are gone,
a:ormx:hosts that were deleted, or a typo in a name. - Delete them, or correct the name.
- Check again.
A null DNS lookup was found for include
Also reported as: SPF void lookup; Include domain does not exist; SPF include returns NXDOMAIN
An include: or redirect= names a domain that doesn't exist. It counts as a void lookup, and because the included record can't be evaluated, receivers return a permerror for the whole record.
- Copy the include again from the provider's current setup instructions; a typo, or an include the provider retired, is the usual cause.
- If you no longer send through that provider, delete the term.
- Check again.
No valid SPF record for included domain
Also reported as: SPF include has no SPF record; Include target missing v=spf1; permerror: include domain has no SPF record
The domain named in include: or redirect= exists but publishes no v=spf1 record. RFC 7208 makes that a permerror once a message's check reaches it, so mail from senders not matched earlier in the record fails SPF.
- Check you used the provider's SPF include, not its website or another of its host names.
- If you no longer send through that provider, delete the term.
- If the include is one of yours, such as
include:_spf.example.com, publish the SPF record at that name.
SPF include loop
Also reported as: SPF record includes itself; Recursive SPF include; SPF redirect loop
An include or redirect leads back to a domain already being evaluated, directly or through another record. Evaluation can never finish, and receivers end it with a permerror.
- Remove any
include:of the domain's own name; it adds nothing. - If two of your records include each other, keep the include in one direction only.
- Check again.
Found duplicate include on SPF path
Also reported as: Duplicate SPF include; Same include appears twice
The same include is reached more than once while evaluating the record: listed twice, or also included by another record you include. Each copy spends lookups and authorizes nothing new.
- Delete the extra copy from your own record.
- If a provider's record already includes it, remove yours.
SPF record uses +all
Also reported as: SPF allows all senders; +all mechanism; Permissive SPF record
+all, or all with no qualifier, matches every server on the internet, so anyone can send mail that passes SPF for this domain. So does an include whose record ends in +all, and an exists: with a fixed name that resolves: it matches whoever sends.
- List your real senders before the end of the record.
- Replace
+allwith~all:v=spf1 include:<your provider's include> ~all.
SPF record ends in ?all (neutral)
Also reported as: Neutral SPF policy; SPF ?all
?all asserts nothing about servers the record doesn't list: their mail gets a neutral result, which receivers treat much like having no SPF.
- Make sure every service that sends as this domain is listed.
- Change the ending to
~all, and to-alllater if you want; see softfail vs fail.
SPF record has no all mechanism
Also reported as: Missing all mechanism; SPF record does not end with all
Without an all or a redirect=, servers the record doesn't list get the default result, neutral, so the record never fails anything.
- End the record with
~allor-all:v=spf1 include:<your provider's include> ~all.
SPF record ends in ~all (softfail)
Also reported as: SPF softfail; ~all vs -all
~all marks mail from unlisted servers as suspicious without failing it outright, and leaves the decision to DMARC. That is a sound setting when DMARC is enforced, so it is reported as a note, not a problem.
- Keep
~allif your DMARC policy is quarantine or reject. - Move to
-allonly once every legitimate sender is listed, and read softfail vs fail for the forwarding trade-off.
SPF record uses the ptr mechanism
Also reported as: ptr mechanism deprecated; Avoid ptr in SPF
ptr makes the receiver look up the sending IP's host name and then confirm it with a forward lookup. RFC 7208 says not to publish it because it is slow and unreliable, and some receivers skip it, so mail it was meant to cover can fail.
- Replace
ptrwith theip4:andip6:ranges, or thea:host, of the servers it was meant to cover. - Check again.
SPF redirect ignored because of all
Also reported as: redirect= used with all; SPF redirect modifier ignored
RFC 7208 §6.1 has receivers ignore redirect= when the record contains an all mechanism anywhere, so the record you redirect to is never used.
- To hand the whole policy to another record, remove the
all:v=spf1 redirect=_spf.example.com. - To keep the policy here, remove the
redirect=and list the senders in this record.
SPF mechanisms after all are ignored
Also reported as: Terms after all; include after ~all
Receivers read terms left to right and stop at all, which matches every sender. Anything after it is never evaluated, so senders listed there aren't authorized.
- Move the terms in front of
all:v=spf1 include:_spf.one.example include:_spf.two.example ~all. - Check again.
SPF record uses macros
Also reported as: SPF macro %{i}; exists:%{i} in SPF
A term contains a macro such as %{i} (the sending IP) or %{d} (the domain), which the receiver fills in for each message. It counts as one lookup, but a checker with no real message can't follow it, so this check doesn't call the record verified and the domain grade shows SPF as partly checked, at no cost. Macros are legitimate, and some large senders publish them with exists:.
- Nothing to fix if you added it on purpose or copied it from a provider's instructions.
- If you don't recognize it, find which service it belongs to before removing it; it may be what authorizes that service's mail.
SPF record has extra quotes or text before v=spf1
Also reported as: SPF record does not start with v=spf1; Quoted SPF record
A TXT record contains v=spf1 but doesn't start with it, usually because literal quote marks or a stray character were pasted into a DNS editor that adds its own quotes. Receivers only accept records that begin with v=spf1, so they ignore this one.
- Open the record in your DNS editor and remove the extra quote marks or leading characters.
- Check what DNS serves:
dig TXT example.com +shortshould print the record inside one pair of quotes, which dig adds itself. - Check again.
Frequently asked questions
How many DNS lookups does SPF allow?
Can a domain have two SPF records?
Should SPF end in ~all or -all?
Does SPF check the From address?
Do subdomains inherit the SPF record?
What is an SPF permerror?
Go deeper: How to fix an SPF record that isn't found · Gmail, Yahoo and Microsoft bulk sender requirements · Sending domains in Unitpost
Free tools
Send from a domain that checks out
Unitpost sets up SPF, DKIM and DMARC with you and keeps checking them.