Skip to content

Deliverability guide

SPF Records Explained: Syntax, the 10-Lookup Limit and Common Mistakes

How an SPF record is read, what each mechanism does, why the ten-lookup limit breaks so many records, and how to choose between ~all and -all once DMARC is in place.

What is SPF?

SPF (Sender Policy Framework) is a DNS TXT record that lists the servers allowed to send email using your domain in the envelope sender address. When a receiving server gets a message, it looks up the SPF record for the domain in the MAIL FROM command (also called the Return-Path or bounce address), checks whether the connecting IP address is on the list, and produces a result such as pass, fail or softfail.

The current specification is RFC 7208. Gmail and Yahoo require every sender to pass SPF or DKIM, and anyone sending in bulk to pass both. Microsoft applies a similar rule to high-volume senders reaching Outlook.com addresses.

One detail causes more confusion than everything else in this guide combined: SPF checks the envelope sender, not the From address your recipients see. If your email platform uses its own bounce domain, SPF passes for the platform’s domain, and your own SPF record isn’t consulted at all. That matters for DMARC, which we cover below.

How a receiving server evaluates SPF

The receiving server takes the domain from MAIL FROM. If the message has an empty envelope sender, as bounce notifications do, it uses the domain from the HELO/EHLO greeting instead. It then fetches the TXT record beginning with v=spf1 for that domain and reads the terms from left to right. The first mechanism that matches the sending IP decides the result. Anything after it is ignored.

If nothing matches and there’s no all at the end, the result is neutral. If the record can’t be evaluated because it’s malformed or too complex, the result is permerror. A DNS timeout produces temperror, which receivers usually treat as “try again later”.

SPF record syntax

A simple record looks like this:

v=spf1 include:_spf.google.com ip4:203.0.113.25 -all

It says: mail is authorized if it comes from Google Workspace’s servers or from the address 203.0.113.25, and anything else should fail.

Mechanisms

  • ip4 and ip6 match a single address or a range in CIDR notation, such as ip4:198.51.100.0/24.
  • a matches the IP addresses in the domain’s A or AAAA records. a:mail.example.com checks a different hostname.
  • mx matches the IP addresses of the domain’s MX hosts. Useful when your inbound mail servers also send, which is less common than people assume.
  • include runs the SPF check against another domain’s record and matches if that check passes. This is how you authorize a third-party platform.
  • exists matches if a constructed domain name returns any A record. It’s used in advanced setups and by some providers.
  • ptr matches via reverse DNS. RFC 7208 says it should not be used. It’s slow, unreliable and some receivers ignore it.
  • all always matches. It belongs at the end and sets the default for everything not listed.

Qualifiers

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

  • + pass (the default when no prefix is written)
  • - fail
  • ~ softfail
  • ? neutral

So include:_spf.google.com and +include:_spf.google.com mean the same thing. In practice you’ll only see qualifiers in front of all.

Modifiers

redirect= hands the whole evaluation to another domain’s record, which is handy when many domains share one policy. It’s only applied if no mechanism matched, and it should not be combined with an all mechanism, because all always matches first. exp= points to an explanation string returned on failure. Few senders use it.

The 10 DNS lookup limit

An SPF check may trigger no more than ten DNS lookups. If evaluating your record needs more, the receiver returns permerror and your SPF effectively fails, often for every message you send.

These terms count toward the limit: include, a, mx, ptr, exists and the redirect modifier. These don’t: ip4, ip6 and all. Crucially, the count is cumulative. An include costs one lookup, and every lookup inside the included record counts too.

Here’s how a record usually drifts over the limit. A company starts with Google Workspace. Marketing adds a newsletter platform, then sales adds a sequencing tool, finance adds an invoicing system, and support adds a helpdesk. Each vendor’s setup page says “add our include to your SPF record”, and each person does exactly that. No single change looks risky. Then one vendor restructures its own SPF record and adds two nested includes, and the total quietly goes from nine to eleven. Your DNS never changed, but your SPF now fails.

There are two related limits that are easy to miss:

  • Void lookups. RFC 7208 recommends receivers allow no more than two lookups that return nothing (no record, or a non-existent domain). An include pointing at a vendor domain that no longer exists can trigger this.
  • MX and PTR address lookups. An mx mechanism can’t resolve more than ten MX hosts. Domains with long MX lists will exceed it.

How to get back under the limit

  1. Remove what no longer sends. Old platforms are the most common cause. Check your DMARC aggregate reports to see which sources still send as your domain before deleting anything.
  2. Remove includes that do nothing for this domain. If a platform sends with its own bounce domain, its include in your record has no effect on SPF for your mail. Many platforms ask for it anyway.
  3. Replace a and mx with ip4/ip6 where the addresses are yours and stable.
  4. Move sending streams to subdomains. If your marketing platform uses news.example.com as its bounce domain, that subdomain has its own SPF record and its own ten-lookup budget.
  5. Flatten with care. Flattening replaces includes with the IP ranges they resolve to. It works, but vendors change their IP ranges without telling you, and a flattened record goes stale. Only flatten with a service or process that refreshes it automatically.

~all vs -all

-all (fail) tells receivers that mail from any unlisted server is unauthorized. ~all (softfail) says it’s probably unauthorized but shouldn’t be rejected on that basis alone.

Before DMARC existed, this choice mattered a lot. Today, for domains that publish DMARC, it matters much less, because large mailbox providers base their decision on the DMARC result, and an SPF fail and softfail both count as “SPF did not pass” for DMARC.

There’s one argument for ~all that’s worth knowing. Some receiving servers reject a hard SPF fail during the SMTP conversation, before DMARC is evaluated. That can block a forwarded message that would otherwise have passed DMARC through a valid DKIM signature. RFC 7489, the original DMARC specification, flagged this exact problem. For that reason, many practitioners use ~all on domains that send real mail and rely on DMARC enforcement for protection.

Our usual approach:

  • Domains that send mail and have DMARC: ~all is a sound default, with DMARC set to quarantine or reject once reports confirm every source is aligned.
  • Domains that never send mail: v=spf1 -all, alongside a DMARC record with p=reject. There’s no legitimate mail to protect, so be strict.
  • Never +all. It authorizes the entire internet to send as you.
  • Avoid ?all except as a deliberate temporary state during a migration.

Why you can only have one SPF record

A domain must have exactly one TXT record starting with v=spf1. If a receiver finds two, the result is permerror, which means SPF fails for every message, including the ones that would have passed under either record on its own.

This is the most common SPF mistake we find, and it’s easy to make. A new vendor’s setup screen says “create a TXT record with this value”, and someone creates a second record instead of editing the first.

The fix is to merge them. If you have:

v=spf1 include:_spf.google.com ~all

v=spf1 include:mail.vendor-example.com ~all

Replace both with one record:

v=spf1 include:_spf.google.com include:mail.vendor-example.com ~all

Then count the lookups again, because merging adds them together. Note that other TXT records, such as domain verification strings, don’t conflict with SPF. Only records starting with v=spf1 count.

SPF and DMARC alignment

For SPF to help your domain pass DMARC, two things must be true: SPF must pass, and the domain in the envelope sender must align with the domain in the visible From address. Under relaxed alignment (the default), they only need to share the same organizational domain, so bounce.example.com aligns with example.com.

When a platform uses its own bounce domain, say bounces.platform-example.net, SPF passes for the platform, but it doesn’t align with example.com. Your DMARC result then depends entirely on DKIM. That isn’t necessarily a problem, because DMARC only needs one of the two to pass and align, but it removes your backup.

Most email platforms let you set a custom return path (sometimes called a custom bounce domain or envelope domain). You add a CNAME, typically on a subdomain, that points to the platform. After that, SPF is checked against your subdomain and aligns with your From domain. Our DMARC guide covers alignment in more detail.

Worked examples

A business that sends only from Google Workspace

v=spf1 include:_spf.google.com ~all

Microsoft 365 plus a server you control

v=spf1 include:spf.protection.outlook.com ip4:203.0.113.25 ~all

A marketing platform with a custom return path

The root domain keeps its own record for staff email:

example.com TXT "v=spf1 include:_spf.google.com ~all"

The platform’s bounce subdomain is a CNAME to the platform, which publishes the SPF record for you. Nothing needs to be added to the root record, and the root stays well under ten lookups.

A parked domain that never sends

v=spf1 -all

Add a DMARC record with p=reject as well. Spammers like to use unprotected lookalike domains that belong to real companies.

Other mistakes worth checking

  • Subdomains don’t inherit SPF. A record on example.com says nothing about mail.example.com. Every hostname used as an envelope sender needs its own record.
  • Long records. A single TXT string can hold up to 255 characters. Longer records must be split into several quoted strings, which receivers join without adding spaces. Put the space inside the quotes, or the two mechanisms merge into one invalid term.
  • Includes of domains with no SPF record. If the included domain doesn’t publish SPF, the result is permerror, not a skipped term.
  • Typos. v=spf1 must be exact and come first. include: _spf.google.com with a space after the colon is invalid.
  • Forwarding. When a message is forwarded, the forwarding server’s IP isn’t in your record, so SPF fails at the final destination. This is expected behavior and the reason DKIM matters. See our DKIM guide.
Does SPF stop people from spoofing my From address?

Not by itself. SPF checks the envelope sender, which recipients never see. Spoofing protection for the visible From address comes from DMARC, which uses SPF and DKIM results and checks alignment.

Do I need SPF if I already have DKIM?

Yes for bulk senders. Gmail and Yahoo require both for anyone sending in bulk, and Microsoft requires both for high-volume senders to Outlook.com. Even below those thresholds, having both gives DMARC two ways to pass.

How do I count my SPF lookups?

Count each include, a, mx, ptr, exists and redirect in your record, then open each included record and count its lookups the same way, recursively. Free SPF checking tools do this automatically and are less error-prone than counting by hand.

Should every vendor include be in my root SPF record?

Only for vendors that send with your root domain as the envelope sender. If a platform uses its own bounce domain or a subdomain you’ve delegated to it, its include in your root record adds a lookup and achieves nothing.

How long do SPF changes take to work?

As long as the record’s TTL. If the old record had a TTL of one hour, receivers may use the cached version for up to an hour. Lower the TTL ahead of planned changes.

Not sure your records are doing their job?

SPF is one checkpoint of several. Our deliverability review looks at every sending source, your DMARC reports and your reputation data together.