SPF Record Syntax: Mechanisms, Limits and Examples

An SPF record is one TXT record listing the servers allowed to send for your domain. Syntax, qualifiers, the 10-lookup limit and real records.

Updated October 19, 20267 min read

An SPF record is a single DNS TXT record, starting with v=spf1, that lists the servers allowed to send mail for your domain. Receivers check the sending IP against it. RFC 7208 caps evaluation at 10 DNS lookups, and a domain may publish only one SPF record.

That is the whole idea. The difficulty is in the details: which mechanism costs a lookup, what the last term means once DMARC is in place, and how big senders keep their records under the limit. This guide works through each one with records we read with dig on 19 October 2026.

What an SPF record looks like

Here is Google's own record, read on 19 October 2026:

$ dig +short TXT google.com | grep spf
"v=spf1 include:_spf.google.com ~all"

Three parts. v=spf1 is the version and must come first. include:_spf.google.com says "also accept anything that record accepts". ~all is the catch-all at the end: anything not matched so far gets a softfail.

The record lives at the domain used in the envelope sender (the MAIL FROM, which ends up in the Return-Path header). It does not live at _spf or any special prefix unless you point to one with include or redirect.

A receiver reads the record left to right. The first mechanism that matches the connecting IP decides the result. Nothing after it is evaluated.

Mechanisms and qualifiers

Mechanisms

Mechanism Matches when Costs a DNS lookup?
ip4: / ip6: The sender's IP is in the listed address or range No
a / a:host The IP equals an A or AAAA record of the domain or host Yes
mx / mx:host The IP belongs to one of the domain's MX hosts Yes
include:domain The other domain's SPF check returns pass Yes
exists:domain The name (often built with macros) resolves to any A record Yes
ptr Reverse DNS of the IP ends in the domain Yes, and RFC 7208 says it SHOULD NOT be published
all Always matches; goes last No

Two modifiers sit beside them. redirect=domain hands the whole check to another domain's record, and it counts as a lookup. exp= points to an explanation string for rejections and does not count.

Qualifiers

Each mechanism can carry a prefix that sets the result when it matches:

  • + pass (the default if you write nothing)
  • - fail
  • ~ softfail
  • ? neutral

So -all means "anything not listed fails", ~all means "anything not listed is probably not us", and ?all says nothing useful. Never publish +all. It authorises every IP on the internet to send as you.

Real SPF records from large senders

These were read with dig +short TXT <domain> on 19 October 2026. Lookup counts come from following every include and redirect down to the bottom.

Domain SPF record (shortened where marked) Lookups
google.com v=spf1 include:_spf.google.com ~all 1
microsoft.com v=spf1 include:_spf-a.microsoft.com include:_spf-b.microsoft.com include:_spf-c.microsoft.com include:_spf-ssg-a.msft.net include:_spf1-meo.microsoft.com -all 7
yahoo.com v=spf1 redirect=_spf.mail.yahoo.com 3
github.com v=spf1 ip4:192.30.252.0/22 include:spf.protection.outlook.com ... include:sendgrid.net ... ~all (8 includes) 10

A few things stand out.

Google's _spf.google.com no longer nests further includes. It now holds the address ranges directly, so google.com spends one lookup in total. Microsoft splits its senders across five includes and ends with -all.

github.com sits exactly on the limit. Its eight includes (Microsoft 365, two Google netblock records, Zendesk, Salesforce, Mailchimp, Marketo and SendGrid) cost eight. Two more come from inside them: Salesforce's record uses exists:%{i}._spf.mta.salesforce.com, and SendGrid's record includes ab.sendgrid.net. That makes 10. One more SaaS tool added to that record would break SPF for every message.

github.com also shows a quirk of TXT records. Its record arrives as two quoted strings, split in the middle of an address: "... ip4:62.253.2" "27.114 ...". RFC 7208 section 3.3 says receivers must join the strings with no space, so this reads as ip4:62.253.227.114. If you split a long record yourself, split it the same way and never add a space at the join.

The 10 DNS-lookup limit and the 2 void-lookup limit

RFC 7208 section 4.6.4 sets two limits.

Ten terms that cause DNS queries. The include, a, mx, ptr and exists mechanisms and the redirect modifier all count, including those inside records you include. ip4, ip6 and all do not. Go over 10 and the receiver "MUST return permerror". Each mx mechanism also has its own sub-limit of 10 address lookups for the MX hosts it finds.

Two void lookups. A void lookup is a query that returns no answer or NXDOMAIN. The RFC says implementations SHOULD stop after two and return permerror. The usual cause is an include that points at a vendor you stopped using, whose SPF record has since been deleted. That stale include does more than waste a lookup: under the include rules in section 5.2, an included domain with no SPF record makes the whole check return permerror.

How to count lookups

Count by hand like this:

  1. Start at 0. Read your record.
  2. Add 1 for every include, a, mx, ptr, exists and redirect.
  3. For each include and redirect, fetch that record with dig +short TXT <name> and repeat steps 2 and 3 inside it.
  4. Ignore ip4, ip6 and all.

For github.com that walk gives 8 at the top level plus 2 nested, which is 10.

Counting by hand gets tedious once vendors nest three deep. The SPF checker does the walk for you and shows the count per include, along with void lookups and duplicate records.

SPF flattening: what you gain and what you risk

Flattening replaces include: terms with the ip4: and ip6: ranges they currently resolve to. Address terms cost no lookups, so a record at 12 can drop to 2.

The trade-off is freshness. When you flatten, you copy the vendor's address list as it was today. Vendors change their ranges without telling you. Google's _spf.google.com alone listed one IPv4 /16, one IPv4 /17 and six IPv6 /56 ranges on 19 October 2026. If Google adds a range next month and your flattened copy does not, mail from that range fails SPF.

Other costs:

  • Size. RFC 7208 section 3.4 says the DNS answer should fit in 512 octets. Flattened records grow fast.
  • Macros cannot be flattened. Salesforce's exists:%{i}... term is evaluated per sending IP, so there is no static list to copy.
  • Someone has to own it. A flattened record needs a scheduled job that re-resolves the includes and republishes on change.

Before flattening, try the cheaper fixes. Remove includes for tools you no longer use. Move a bulk sender to its own subdomain (for example mail.example.com) with its own SPF record, so it spends its own 10 lookups. And check whether a vendor needs SPF on your domain at all: many ESPs send with their own envelope domain and align through DKIM instead.

~all or -all when DMARC is in place

Our scan of the Tranco top 1,000 domains on 19 October 2026 found this split in the final SPF term:

SPF ending Domains (of 1,000) Mail domains (of 688 with MX)
-all (fail) 430 339
~all (softfail) 266 254
?all (neutral) 19 13
redirect= only 34 32
No SPF record 247 46

The large sites are split, and that is reasonable. DMARC only asks whether SPF returned pass for an aligned domain. Softfail and fail are both "not pass", so with DMARC at p=reject a spoofed message fails either way. Among the 346 mail domains at p=reject in the scan, 176 end SPF with -all and 146 with ~all.

The difference shows up before DMARC runs. RFC 7489 section 10.1 warns that some receivers apply SPF first, so -all "could cause that rejection to go into effect early in handling, causing message rejection before any DMARC processing takes place." Forwarded mail is the usual victim: the forwarder's IP is not in your record, SPF fails, and the message is rejected even though its DKIM signature would have passed DMARC. RFC 9989, the Standards Track DMARC specification published in May 2026 that obsoletes RFC 7489, keeps the warning in section 7.1 and adds that mail rejected this early "will never appear in aggregate DMARC reports".

A practical rule:

  • No DMARC yet, or p=none: -all gives receivers the clearer signal.
  • DMARC at quarantine or reject with DKIM signing everything: ~all is fine, and it avoids early rejection of forwarded mail.
  • Domains that never send mail: v=spf1 -all, plus a DMARC record at p=reject.

Multiple SPF records: an instant permerror

RFC 7208 section 4.5 says that if more than one v=spf1 record remains after the lookup, the check "produces the permerror result". It does not merge them and it does not pick one.

This usually happens when a second vendor's setup guide says "add this TXT record" and someone adds it next to the old one. Three domains in our top 1,000 scan had exactly this problem on 19 October 2026.

The fix is one record that holds both sets of terms:

; wrong: two records
example.com.  TXT  "v=spf1 include:_spf.google.com ~all"
example.com.  TXT  "v=spf1 include:sendgrid.net ~all"

; right: one record
example.com.  TXT  "v=spf1 include:_spf.google.com include:sendgrid.net ~all"

Other TXT records on the same name (site verification tokens, for example) are fine. Only records that start with v=spf1 count.

Where SPF stops

SPF has two gaps you should know before relying on it.

It checks the envelope domain, not the From header people read. An attacker can pass SPF for their own domain while showing yours in From. Only DMARC alignment closes that gap; see the complete DMARC guide.

It also breaks on forwarding, because the forwarder's IP is not in your record. That is why DMARC accepts a pass from either SPF or DKIM. Sign everything with DKIM so forwarded mail still has a path to pass.

Gmail sets a floor too: its sender guidelines require SPF or DKIM for every sender, and both for anyone sending 5,000 or more messages a day to Gmail accounts. The sender requirements post covers the rest.

Next step

Run your domain through the SPF checker and write down the lookup count. If it is 8 or more, list each include and the team that owns it before you add another vendor. Then confirm the domain has a DMARC record with the DMARC checker, because SPF on its own says nothing about the From address your recipients see.

Frequently asked questions

Can a domain have two SPF records?

No. RFC 7208 section 4.5 says that if a lookup returns more than one record starting with v=spf1, the check returns permerror. Merge every sender into one record instead.

Should I use ~all or -all?

Once DMARC is at quarantine or reject, either works, because DMARC treats softfail and fail the same way: not a pass. -all is the stricter statement, but RFC 7489 warns that some receivers act on it before DMARC runs, which can reject forwarded mail that DKIM would have saved.

What happens when an SPF record needs more than 10 DNS lookups?

The receiver must stop and return permerror (RFC 7208 section 4.6.4). Under DMARC that counts as an SPF failure, so mail that only had SPF to rely on fails DMARC.

Does SPF check the From address people see?

No. SPF checks the envelope sender (the MAIL FROM or Return-Path domain) and optionally the HELO name. DMARC is what ties the SPF result to the visible From domain through alignment.

spfsender policy frameworkspf recorddns recordsemail spoofing

Check your own domain

Run the free DMARC, SPF and DKIM checks, or start a 14-day trial of DMARC+ reporting.