SPF: which servers may send for your domain.
An SPF record is a line of DNS that lists the servers allowed to send mail for a domain. A receiving server reads it, compares the address that connected against the list, and records pass, fail, softfail or neutral. That is the whole mechanism. What people get wrong is which domain is being checked, how many DNS lookups a record is allowed to cost, and what SPF proves about the From address a reader sees, which is less than it seems. The setup walkthrough for all three records is on SPF, DKIM and DMARC; this page is SPF itself.
What SPF checks: the envelope sender, not the From.
A message carries two sender addresses. The envelope sender is the address given in the SMTP MAIL FROM command, which the receiving server stores as the Return-Path header and uses for bounces. The header From is the address written inside the message, the one a mail client shows. They can differ, and for a sending service they usually do: the envelope sender is on a subdomain the service controls, so bounces come back to it, while the header From is your address.
SPF checks the envelope sender. Per RFC 7208, a verifier evaluates the HELO name first if it wants to, and must evaluate the MAIL FROM identity unless the HELO check already gave a definitive answer. The header From is never consulted. So when a service asks you to publish SPF on mail.yourdomain.com, that is the domain it will put in the envelope, and that is the record the receiver reads.
Record syntax, with a worked example.
An SPF record is a TXT record whose text starts with v=spf1, followed by terms separated by spaces, evaluated left to right until one matches. Here is a company that sends from its own mail server range and from Google Workspace, and wants everything else refused:
acme.com. IN TXT "v=spf1 ip4:203.0.113.0/24 include:_spf.google.com -all"
Read it as: any address in 203.0.113.0/24 passes; otherwise go and evaluate Google’s own record and pass if it does; otherwise fail. Each term is a mechanism, optionally prefixed by a qualifier.
| Term | What it matches | DNS lookups |
|---|---|---|
ip4:203.0.113.0/24 | Any address in this IPv4 range is authorised. | None |
ip6:2001:db8::/32 | The same for an IPv6 range. | None |
a | The A or AAAA addresses of the domain (or of the name given, a:mx1.acme.com). | One |
mx | The addresses of every MX host of the domain. | One, plus up to ten address lookups |
include:other.example | Evaluate that domain's record; a pass there is a match here. | One, plus whatever that record spends |
exists:name | Matches when the name resolves at all. Rare; used for macro tricks. | One |
ptr | Reverse lookup of the sending IP. Deprecated by the RFC; do not publish it. | One, plus address lookups |
all | Matches everything. Goes last, with the qualifier that says what to do. | None |
redirect=other.example | A modifier, not a mechanism: use that domain's record instead of the rest of this one. | One |
Qualifiers: what a match means.
The character in front of a mechanism says what result a match produces. It is optional and defaults to +, so include: and +include: are the same thing. In practice the qualifier only ever matters on the final all, because that is the term that catches everything the list did not.
| Qualifier | Result | Meaning |
|---|---|---|
+ (or nothing) | pass | The sender is authorised. |
- | fail | The sender is not authorised; the receiver may reject. |
~ | softfail | Probably not authorised; accept, but mark it. |
? | neutral | The domain says nothing either way. |
Why do sending services tell you to end with ~all rather than -all? Because the record they ask for sits on a name they will be sending from, and they cannot see what else you will put on it later. A hard fail turns a forgotten sender into rejected mail; a soft fail turns it into a mark the receiver weighs along with everything else. Once DMARC is published, its policy decides what happens to mail that fails authentication, and the SPF qualifier stops carrying the decision on its own.
The ten-lookup limit, and how includes spend it.
RFC 7208 section 4.6.4 limits the terms that cause a DNS query to ten per evaluation: include, a, mx, ptr, exists and the redirect modifier count; ip4, ip6 and all do not. Cross the limit and the result is permerror, which receivers treat as if the domain had no valid SPF at all. The limit exists so a record cannot be used to make a receiver do unbounded work.
The part that catches people is that an include is not one lookup. It is one lookup for the record it names, plus every lookup that record makes in turn, recursively. A typical office suite’s include is two or three deep. Add a CRM, a helpdesk, a newsletter tool and a transactional service to the same record, each with its own nest of includes, and ten arrives quickly:
v=spf1 include:_spf.google.com include:sendgrid.net include:spf.protection.outlook.com a mx -all
^ 1 lookup (+2 inside) ^ 1 (+1 inside) ^ 1 (+2 inside) ^ 1 ^ 1
total so far: 10, and the next include is a permerror
To count yours, resolve each include and add up what it pulls in; several free SPF checkers do this and show the tree. To get back under the limit, remove services you no longer send from first. Then replace an include with the provider’s published ip4 and ip6 ranges only where the provider commits to stable ranges, because a flattened record goes stale the day the provider moves. The cleanest fix is structural: give each sending service its own subdomain with its own record, so no single name has to carry them all. That is what a mail subdomain does.
Two smaller limits sit beside the big one. Each mx mechanism may resolve at most ten address records, and a verifier should stop after two lookups that return nothing. Keep the record small enough for a UDP answer as well, which the RFC puts at roughly 450 octets for the whole DNS response.
One record per name.
A name must not have more than one SPF record. RFC 7208 is explicit that a receiver finding two returns permerror, so a second v=spf1 TXT record does not add a sender, it disables authentication for the whole domain. When a provider’s instructions say “add this SPF record” and one already exists, merge the new include into the existing record. A record on a different name is a different record, which is the other reason a subdomain per service is the safe pattern.
The record Notix asks for.
When you add a domain in the dashboard, the DNS records it shows include a TXT record on the mail subdomain of that domain:
mail.acme.com. IN TXT "v=spf1 include:amazonses.com ~all"
Notix puts mail.yourdomain.com in the envelope sender of everything it sends for you, so this is the record receivers evaluate. An MX record on the same name routes bounces back to the infrastructure Notix sends through, and the DKIM record on notix._domainkey signs the message so the visible From is covered too. Your bare domain’s SPF record, and the office suite already listed in it, are not touched. The dashboard checks the records after you add them and shows each one as pending or verified; the same status is on the domain in the API.
What SPF does not do.
SPF proves that the server which delivered a message was allowed to send for the domain in the envelope. It proves nothing about the From address a reader sees, because that address is not checked. A spoofer can put a domain they own in the envelope, pass SPF for it, and write your domain in the header From. SPF also breaks on plain forwarding, because the forwarding server is not in your list, which is one reason receivers weigh a softfail lightly.
The fix for the first problem is alignment, which is DMARC’s job: it requires the domain that passed SPF, or the domain whose DKIM signature verified, to match the visible From. The fix for the second is DKIM, whose signature survives forwarding when SPF cannot. SPF is a third of the answer, and it is the third that is easiest to publish and easiest to get wrong. The other two are on DKIM and DMARC, and all three are set up together on SPF, DKIM and DMARC. The rules quoted here are from RFC 7208.
Questions, answered.
What does an SPF record actually check?
Why does SPF say too many DNS lookups, and how do I fix it?
Should I end the record with ~all or -all?
Can a domain have two SPF records?
Does SPF stop someone spoofing my From address?
What SPF record does Notix ask me to publish?
Keep reading.
Set up SPF, DKIM and DMARC
The three DNS records a sending domain needs, what each one proves, and the one-click path when your DNS provider supports Domain Connect.
LearnDKIM selectors and keys
How a DKIM signature is checked, what the selector in the DNS name is for, and why each sending service gets its own.
LearnDMARC record examples
Records for p=none, quarantine and reject, what alignment means, and how to read the aggregate reports before tightening.
ProductSMTP relay
Point any framework's mailer at one host and port; the relay turns SMTP into the same tracked, suppressed send.
NotixDocs
The quickstart: verify a domain, copy an API key, send the first email.
Add a domain and the records are written for you.
Notix shows the exact SPF, DKIM and DMARC records for each domain and checks them until they verify.