What it is
SPF is a single TXT record on your domain that names the servers permitted to send mail as you. When a receiving server gets a message claiming to be from your domain, it looks up that record and checks whether the connecting server is on the list.
A minimal record looks like v=spf1 include:_spf.google.com -all. The include: delegates to Google’s own list of sending servers, and -all says everything else should be rejected.
Why it exists
SMTP was designed with no way to verify who a message was from. Anyone can connect to a mail server and claim to be sending as your domain. SPF was the first widely adopted attempt to close that hole, by letting a domain owner publish who is actually authorised.
The ten-lookup limit
This is the part that catches people. SPF allows a maximum of ten DNS lookups when a receiver evaluates your record. Every include: costs one, and every include nested inside those costs another. Google, a sending tool, a CRM and a helpdesk together will usually push you over.
Past ten lookups, SPF returns permerror. Depending on the receiver, that can be treated as an outright failure. Nothing warns you and the record still looks perfectly reasonable in a text editor, which is why this can sit broken for months.
What -all, ~all and +all mean
-all is hard fail: reject anything not listed. ~all is soft fail: accept but mark suspicious. +all authorises the entire internet to send as you and should never appear in a record.
Most domains sit on ~all because it feels safer. It is not — it tells receivers you are not confident about your own sender list. Move to -all once every legitimate sender is accounted for.
Record syntax, mechanism by mechanism
An SPF record is one TXT string that starts with v=spf1 and ends with an all qualifier. Everything between is a list of mechanisms the receiver evaluates left to right, stopping at the first match.
ip4:/ip6:— a literal address or CIDR block. Costs no lookup, which is why flattened records are built from these.include:— pull in another domain's SPF record. This is how you authorise a provider:include:_spf.google.comfor Workspace,include:spf.protection.outlook.comfor Microsoft 365. Each one costs a lookup, plus whatever lookups the included record makes.a/mx— authorise whatever your A or MX records point at. Each costs a lookup and is rarely what you actually mean on a sending domain.redirect=— replace this record with another domain's. Use it when many domains share one policy; it counts as a lookup.-all/~all/?all— what to do with everything not matched. Hard fail, soft fail, neutral.
A cold email domain on Google Workspace needs exactly this and nothing else: v=spf1 include:_spf.google.com ~all. On Microsoft 365: v=spf1 include:spf.protection.outlook.com -all. Every additional mechanism is a lookup spent and a sender you may not have meant to authorise.
Common failure modes
Two records. A second v=spf1 TXT on the same name is a permerror on every check. It usually arrives with a website builder or a hosting panel that adds its own. There must be one; the SPF merger combines them without dropping a sender.
Eleven lookups. The record was fine when it had Google in it. Then a CRM, a helpdesk and a marketing tool each added an include, some of which nest two or three deep, and the count is over ten without anyone touching the record. Permerror again, and it fails quietly. The lookup counter shows the whole tree with the cost of each branch.
+all. Authorises every server on the internet to send as you. It passes every check, which is why people leave it, and it is a stronger negative reputation signal than having no record at all.
A 255-character string that was never split. DNS TXT records are stored in chunks of 255 characters. Most panels split long records for you; some do not, and the record silently truncates. If the checker shows your record cut off mid-mechanism, this is why.
The subdomain has no record. SPF does not inherit. mail.acme.com sending with no SPF of its own is unauthenticated even if acme.com is perfect. Tracking and bounce subdomains that send anything need their own record.
Domains that should never send
A parked secondary domain, a redirect-only domain, a domain you bought for the name and nothing else — each one is spoofable until you say otherwise. Publish v=spf1 -all on every domain that sends no mail, and a DMARC record at p=reject beside it. It takes a minute, it stops your fleet's unused domains being used against you, and it is one of the checks the domain checker runs across a whole list at once. The email DNS setup tool generates the sending or the non-sending record set per domain and can push it to Cloudflare in bulk.
Check yours
These run free in your browser. Nothing you type reaches a server.
Common questions
Can I have two SPF records?
No. A domain must have exactly one SPF TXT record. Two records is a permanent error and receivers will treat authentication as broken. Multiple senders go into one record as multiple include: mechanisms.
Does SPF alone protect my domain?
No. SPF validates the envelope sender, not the From address your recipient actually sees. Someone can pass SPF on their own domain while displaying yours in the From field. That gap is what DMARC alignment closes.
What happens if I go over ten lookups?
Receivers return permerror, and many treat that as a fail. Fix it by removing includes for services that no longer send, consolidating where possible, or moving a service onto a subdomain with its own SPF record.
Should a cold email domain use -all or ~all?
Either works with DMARC in place, because DMARC is what actually enforces. -all is stricter and is the right default on Microsoft 365. Google's own guidance shows ~all for Workspace, and some receivers handle a soft fail more gracefully when mail is forwarded. On a domain with DMARC at quarantine or reject, the difference is small; what matters is that the record exists, has one all qualifier, and stays under ten lookups.
Does a tracking subdomain need its own SPF record?
Only if it sends. A click-tracking subdomain serves redirects and never sends mail, so it needs no SPF. If your sending tool uses a subdomain as the bounce address (the Return-Path), that subdomain does send in the SPF sense and needs a record authorising the tool. The tool's setup guide will say which; check the Return-Path on a real sent message with the header analyzer if you are not sure.
Related concepts
If this is the thing going wrong
The pages explain it. If you would rather it was simply fixed, that is the work I do.