SPF (Sender Policy Framework) is the standard by which a domain owner declares, in a DNS TXT record starting with v=spf1, which servers are authorised to send mail for that domain. The receiving server looks up the SPF record of the domain in the message's envelope sender and checks whether the sending IP address is on the list. It is defined in RFC 7208.
This article covers how to write a correct SPF record, what the all at the end means, and the 10 DNS lookup limit that silently breaks records.
What does an SPF record look like?
An example for a domain that uses Microsoft 365 and also sends from its own MX server:
ornek.com.tr. IN TXT "v=spf1 mx include:spf.protection.outlook.com -all"
The record is read left to right; the first matching mechanism decides the result:
| Mechanism | Meaning |
|---|---|
ip4: / ip6: | A specific IP address or range is authorised. |
a | The addresses in the domain's A/AAAA record are authorised. |
mx | The domain's MX servers are authorised. |
include: | Another domain's SPF record is evaluated too (e.g. the mail provider). |
all | The result to apply if nothing above matched. |
What does all at the end of the record mean?
The qualifier in front of all decides what happens to a sender that is not on the list:
-all(fail): an unlisted sender is not authorised. This is the recommended value.~all(softfail): possibly unauthorised; receivers usually still accept the message. It only has an effect when the DMARC policy isquarantineorreject.?all(neutral): says nothing. Spoofed mail is not blocked.+all(pass): every server in the world is authorised. This is worse than having no SPF record at all: spoofed mail passes SPF and looks trustworthy.
A record with no all at the end behaves like ?all.
What is the 10 DNS lookup limit in SPF?
RFC 7208 limits the total number of mechanisms and modifiers that require a DNS query to at most 10 per SPF evaluation. Counted: include, a, mx, ptr, exists and redirect. Not counted: ip4, ip6 and all.
The trap is that include chains are counted recursively. You see four include entries in your own record, but the providers' records contain their own include entries. Once the total exceeds 10, the result is permerror: the receiver treats SPF as invalid, your legitimate mail can fail SPF and, if DMARC is enforcing, be rejected.
To reduce the lookup count:
- Remove the
includeentries of services you no longer use. - Write systems that send from a fixed IP address (e.g. an application server) as
ip4:. - Send high-volume services such as newsletters or CRM from a separate subdomain (e.g.
newsletter.ornek.com.tr) with its own SPF record.
Lookups that return nothing (NXDOMAIN or no record) are limited too: RFC 7208 recommends limiting these "void lookups" to 2. The include of a deleted service can use up that limit.
How do you write an SPF record? Step by step
- List everything that sends. Your mail provider (Microsoft 365, Google Workspace), your own mail server, CRM, newsletter service, invoicing system, scan-to-mail printers, your website's contact form.
- Pick the right mechanism for each. For providers, the
include:value from their own documentation; for fixed servers,ip4:/ip6:. - Write a single record. There must not be a second record starting with
v=spf1at the same name. Two records meanpermerrorunder RFC 7208, and receivers ignore SPF. When adding to an existing record, edit it; do not add a new one next to it. - End with
-all. If unsure, use~allduring the transition, but switch to-allonce DMARC is enforcing. - Check the length. A single TXT string is at most 255 characters; a longer record is split into several quoted strings. Many panels do this for you.
- Verify. To confirm the record is published:
nslookup -type=TXT ornek.com.tr
dig +short TXT ornek.com.tr
For a domain that sends no mail, the only thing to do is shut down spoofing:
ornek.com.tr. IN TXT "v=spf1 -all"
Which SPF mechanisms should you avoid?
ptr: RFC 7208 recommends against using it. It is slow, generates many DNS queries and some receivers do not evaluate it at all.
Unneeded a and mx: if your web server does not send mail, the a mechanism authorises it for nothing and uses up a lookup. If mail leaves through Microsoft 365 or Google Workspace, mx is often unnecessary too; the provider's include: value already covers the sending servers.
Wide IP ranges: a range written with ip4: should contain only your own servers. Listing a shared hosting company's whole range authorises the other customers in that range to send as you.
Confusing redirect= with include:: redirect= hands over the policy of another domain entirely and is ignored if the record contains all. It is used to point several domains at one central SPF record; to add a provider, include: is the right tool.
Is SPF enough on its own?
No. SPF only checks the envelope sender; it does not look at the From address the user sees. When a message is forwarded through another server, the sending IP changes and SPF breaks. A DKIM signature and DMARC alignment close both gaps. More: What is DMARC? and What is DKIM?
What does Denetta check in SPF?
Denetta's Domain and Email Security module reads the SPF record from DNS together with its include chain and evaluates it with these rule codes:
- MAIL-001: no SPF record.
- MAIL-002: the SPF record is invalid (
permerror): multiple records, a syntax error, a loop or the void-lookup limit. - MAIL-003: the record authorises everyone (
+all). - MAIL-004: the record does not reject spoofed senders (
?all, noall, or~allwithout DMARC enforcement). - MAIL-005: the 10 DNS lookup limit is exceeded; the chain is counted recursively.
Remediation text is written for your mail provider (e.g. include:spf.protection.outlook.com for Microsoft 365) and for your DNS hosting provider's panel. The text is only displayed; Denetta never changes a DNS record.
You can see your own domain's SPF status with the domain security check. It reads only DNS records and produces a pre-check score from 21 rules; the full module has 31 rules.
Frequently asked questions
Can a domain have two SPF records?
No. A second TXT record starting with v=spf1 at the same name is a permerror under RFC 7208, and receivers ignore SPF. All senders must be merged into a single record.
Should I use ~all or -all?
Use -all once you are sure every sending service is listed. ~all (softfail) only protects when the DMARC policy is quarantine or reject; on its own it does not stop spoofed mail.
What happens if an SPF record exceeds 255 characters?
A single TXT string can be at most 255 characters; a longer record is split into several quoted strings, which receivers concatenate. Many DNS panels split the value for you.
My SPF record looks right but mail is still rejected. Why?
Common causes: exceeding the 10 DNS lookup limit (permerror), a second SPF record at the same name, or a sender that is not listed (CRM, newsletter, printer). DMARC reports show which one it is.