What Does an SPF Record Look Like?
An SPF record is a single DNS TXT record published at your domain's root. It starts with the version tag v=spf1, lists mechanisms that identify your authorized sending servers, and ends with an all mechanism that tells receivers what to do with everything else.
example.com. 3600 IN TXT "v=spf1 include:_spf.google.com include:sendgrid.net ip4:203.0.113.10 -all"
Read left to right, this record says: mail from Google Workspace is authorized, mail from SendGrid is authorized, mail from the server at 203.0.113.10 is authorized, and everything else should fail. Receivers evaluate mechanisms in order and stop at the first match — order matters.
Per RFC 7208 §3.2, a domain MUST NOT publish multiple SPF records. If a receiver finds two records starting with v=spf1, the check returns PermError and authentication fails for all your mail — this is the single most common mistake when people paste a generator's output without checking what's already in DNS. If SPF is new to you, start with what an SPF record is and how it works, then come back here to build one.
Step 1: Inventory Every Service That Sends Your Email
A generator can only encode the list you give it — the inventory is the real work. Missing a sender means that service's mail starts softfailing or bouncing the moment you publish. Walk through these categories:
- Mailbox provider: Google Workspace, Microsoft 365, Zoho — the service hosting your user inboxes.
- Transactional email: SendGrid, Amazon SES, Postmark, Mailgun — password resets, receipts, app notifications.
- Marketing platform: Mailchimp, Klaviyo, HubSpot, Brevo — newsletters and campaigns.
- CRM and helpdesk: Salesforce, Zendesk, Intercom — anything that sends "on behalf of" your domain.
- Your own servers: any VPS, on-prem server, printer, or monitoring system that sends mail directly.
For each service, use the include: value from its official documentation — never a blog post, because these values change. The big ones as of this writing:
| Service | SPF mechanism | Source |
|---|---|---|
| Google Workspace | include:_spf.google.com | Google Workspace Admin Help |
| Microsoft 365 | include:spf.protection.outlook.com | Microsoft 365 SPF docs |
| SendGrid | include:sendgrid.net | SendGrid domain authentication docs |
| Amazon SES | include:amazonses.com (when using a custom MAIL FROM) | AWS SES developer guide |
| Mailchimp | include:servers.mcsv.net | Mailchimp domain verification docs |
| Zoho Mail | include:zohomail.com (region-specific variants exist) | Zoho Mail SPF docs |
Not sure what's actually sending as your domain? Pull the Return-Path and Received headers from real messages with the Email Header Analyzer — the Return-Path domain is what SPF actually checks. If you already have DMARC reporting enabled, your aggregate reports list every source IP claiming to be you.
Step 2: Assemble the Record — SPF Syntax Reference
Every SPF record is built from mechanisms defined in RFC 7208 §5. This is the complete set a generator chooses from:
| Mechanism | What it authorizes | DNS lookup? |
|---|---|---|
ip4:203.0.113.10 / ip4:203.0.113.0/24 | A specific IPv4 address or CIDR range | No |
ip6:2001:db8::1 | A specific IPv6 address or range | No |
include:_spf.example.net | Every sender authorized by another domain's SPF record | Yes |
a / a:mail.example.com | The IP(s) in a domain's A/AAAA records | Yes |
mx / mx:example.com | The IPs of a domain's MX hosts | Yes |
exists:%{i}.spf.example.com | Matches if a constructed hostname resolves (macro-based) | Yes |
ptr | Reverse-DNS match — deprecated, do not use | Yes |
all | Everything — always matches; must be last | No |
Interpretation: prefer ip4/ip6 for servers you control (zero lookups), use include: for third-party services, and never let a generator emit ptr — RFC 7208 §5.5 says it "SHOULD NOT" be used because it is slow, unreliable, and burns lookups.
Assembly rules:
-
Start with
v=spf1. Exactly once, lowercase, no leading space. -
Order mechanisms by volume. Evaluation stops at the first match (RFC 7208 §4.6.2), so put your highest-volume sender first to keep receiver-side evaluation short.
-
Separate every term with a single space. No commas, no semicolons.
-
End with exactly one
allterm. Anything written afterallis ignored.
A finished record for a domain using Microsoft 365, Mailchimp, and one self-hosted app server:
example.com. 3600 IN TXT "v=spf1 include:spf.protection.outlook.com include:servers.mcsv.net ip4:198.51.100.25 -all"
Step 3: How Many DNS Lookups Does Your SPF Record Use?
This is where most generated records fail in production. Per RFC 7208 §4.6.4, receivers MUST limit SPF evaluation to 10 DNS-querying terms — include:, a, mx, ptr, exists, and the redirect= modifier all count. Exceed 10 and the result is PermError: your mail fails authentication even though every sender in the record is legitimate.
The trap is that includes chain. include:_spf.google.com is not 1 lookup — Google's record contains three nested includes, so it costs 4. Count the whole tree:
v=spf1 include:_spf.google.com include:sendgrid.net include:servers.mcsv.net mx -all
_spf.google.com 1 lookup
└─ _netblocks.google.com +1
└─ _netblocks2.google.com +1
└─ _netblocks3.google.com +1
sendgrid.net 1 lookup
servers.mcsv.net 1 lookup
mx 1 lookup
─────────
Total 7 of 10
Three lookups of headroom sounds fine until the next tool your marketing team signs up for adds a 3-deep include chain. If you're at 8 or more, flatten now:
- Replace
aandmxwith literalip4:/ip6:mechanisms — resolve them once with the DNS Lookup tool and paste the IPs. Zero lookups. - Remove includes for services you no longer use. Old ESP includes are the most common dead weight.
- Split senders across subdomains (e.g., marketing mail from
news.example.comwith its own SPF record) — each domain gets its own 10-lookup budget.
RFC 7208 §4.6.4 also caps void lookups — queries that return NXDOMAIN or an empty answer — at 2. A typo'd include like include:_spf.gogle.com doesn't just waste a lookup; it counts toward the void limit and can PermError the record on its own. If you're already seeing that error, the SPF PermError fix guide covers diagnosis step by step.
Step 4: Choose the Right all Qualifier
The final all mechanism carries a qualifier from RFC 7208 §4.6.2 that sets your policy for unlisted senders:
| Record ending | Result for unlisted senders | When to use |
|---|---|---|
-all (fail) | Receiver should reject/junk | Final state — sender inventory is complete and verified |
~all (softfail) | Accept but mark suspicious | Rollout phase while you confirm nothing is missing |
?all (neutral) | No assertion at all | Almost never — provides no protection |
+all (pass) | Everything on the internet is authorized | Never — this authorizes spoofers |
+all is not a permissive default; it is a critical misconfiguration that tells every receiver that any IP on earth may send as your domain. Some generators still offer it — refuse. Deploy with ~all, watch your DMARC aggregate reports for one to two weeks, then tighten to -all once every legitimate source passes. Note that under DMARC, SPF softfail and fail are treated the same for alignment purposes, so ~all vs -all mainly affects receivers evaluating SPF standalone.
Step 5: Publish the TXT Record in DNS
Log in to the DNS provider that hosts your zone (where your nameservers point — confirm with the DNS Lookup tool if unsure) and create one TXT record:
Type: TXT
Host: @ (the domain root — some providers want it blank or the full domain)
Value: v=spf1 include:spf.protection.outlook.com ip4:198.51.100.25 -all
TTL: 3600
Three publishing rules that generators can't enforce for you:
-
Check for an existing SPF record first. Run
dig txt example.comor use the DNS Lookup tool. If av=spf1record already exists, edit it — merge your new mechanisms into the existing record. Two SPF records = PermError on everything (RFC 7208 §3.2). -
Use TXT, not the deprecated SPF record type. RFC 7208 §3.1 discontinued the type-99 SPF RR; publish TXT only.
-
Mind the 255-character string limit. A single TXT character-string caps at 255 bytes. Longer records must be split into multiple quoted strings inside one record — receivers concatenate them per RFC 7208 §3.3:
"v=spf1 include:..." " include:... -all". Most DNS control panels handle the split automatically, but verify the published result. More on TXT record mechanics in the DNS TXT records guide.
Step 6: Validate Before and After Publishing
Verify from the command line that the record resolves as one clean string:
$ dig +short txt example.com
"v=spf1 include:spf.protection.outlook.com ip4:198.51.100.25 -all"
Then run the domain through the SPF Checker — it parses the record, walks every nested include, counts total DNS lookups against the 10 limit, and flags dangerous qualifiers like +all. Do this immediately after publishing and again after any DNS change; allow for your old record's TTL before expecting every resolver to see the update.
Finally, send a real message to a mailbox you control at Gmail or Outlook and inspect the Authentication-Results header (the Email Header Analyzer will parse it for you):
Authentication-Results: mx.google.com;
spf=pass (google.com: domain of bounce@example.com designates 198.51.100.25 as permitted sender)
spf=pass with your sending IP named is the finish line. Remember SPF checks the Return-Path (envelope-from) domain, not the From: header — services that send through their own bounce domain will need SPF on that domain plus DKIM for DMARC alignment.
Stop checking manually. DNSnexus Monitor watches your DNS records, SSL expiry, email auth, and blacklist status — and alerts you before things break. Free tier covers one domain.
Common Generator Mistakes That Break SPF
- Publishing a second SPF record instead of merging with the existing one — instant PermError (RFC 7208 §3.2).
- Trusting the lookup count of the flat record. Generators that don't recurse into includes will tell you "3 includes = 3 lookups" when the real total is 9.
- Typos in include hostnames (
inlcude:,_spf.gogle.com) — a misspelled mechanism is a syntax error; a misspelled hostname burns void lookups. - Keeping
~allforever. Softfail is a rollout state, not a policy. Pair the finished record with DMARC enforcement to make it mean something. - Forgetting SPF is one-third of the stack. Modern deliverability — including Google and Yahoo bulk-sender requirements — needs SPF and DKIM and DMARC. Verify your DMARC policy with the DMARC Checker and confirm your inbound mail routing with an MX Lookup while you're in DNS.
Key Takeaways
- ✓ One SPF record per domain, published as a TXT record at the root, starting
v=spf1— two records cause PermError per RFC 7208 §3.2. - ✓ The 10-DNS-lookup limit (RFC 7208 §4.6.4) counts the entire include tree;
include:_spf.google.comalone costs 4 lookups. - ✓ Prefer
ip4:/ip6:for your own servers (zero lookups); useinclude:only for third-party services, from their official docs. - ✓ End with
~allduring rollout,-allonce verified — never+all, which authorizes the whole internet. - ✓ Validate the live record with the SPF Checker after every change; confirm
spf=passin a real message's Authentication-Results header.
Frequently Asked Questions
Q: How do I create an SPF record from scratch?
List every service that sends email for your domain, get each one's official include: or IP mechanism, and combine them into one TXT record: v=spf1 include:provider1 ip4:your-server-ip ~all. Publish it at host @ in your DNS, then validate lookups and syntax with an SPF checker.
Q: How many DNS lookups can an SPF record have?
Ten. Per RFC 7208 §4.6.4, receivers must stop after 10 DNS-querying terms — every include:, a, mx, ptr, exists, and redirect= counts, including ones nested inside includes. Exceeding the limit returns PermError, which fails authentication for all your mail.
Q: Can I have two SPF records on one domain?
No. RFC 7208 §3.2 forbids multiple SPF records; receivers that find two v=spf1 TXT records return PermError and your mail fails authentication. If a record already exists, merge your new mechanisms into it instead of adding a second record.
Q: What is the difference between ~all and -all?
-all (fail) tells receivers to reject or junk mail from unlisted senders; ~all (softfail) says accept it but treat it as suspicious. Use ~all while confirming your sender inventory, then move to -all. For DMARC alignment purposes, softfail and fail are evaluated the same.
Q: Does an SPF record work without DKIM and DMARC?
SPF alone validates only the envelope sender and breaks on forwarded mail. Google and Microsoft bulk-sender requirements expect SPF plus DKIM, with DMARC to tie both to the visible From address. Deploy all three; SPF is the first step, not the whole job.
Q: Why does my generated SPF record still fail with PermError?
The usual causes are more than 10 total DNS lookups once nested includes are counted, a second SPF record already published on the domain, or a typo'd include hostname triggering the void-lookup limit. Walk the include tree with an SPF checker to find which one applies.
Q: What does an SPF record look like for Google Workspace?
Google's official record is v=spf1 include:_spf.google.com ~all, published as a TXT record at your domain root. Note that Google's include chain uses 4 of your 10 DNS lookups, so budget accordingly before adding more services to the same record.
Next Steps
Your record is only correct until the next service signs up to send as your domain. Run the SPF Checker now to see your live record's lookup count and qualifier, then complete the authentication stack: verify DKIM signing with the DKIM guide, confirm your DKIM selector resolves correctly with the DKIM Selector Explained guide, set an enforcement policy with the DMARC Checker and the DMARC policy guide, and if anything returns PermError, the SPF PermError fix guide walks through every cause. Once DMARC reports start arriving, how to read a DMARC aggregate report explains how to spot which senders are failing alignment. For the DNS mechanics underneath it all, see how DNS TXT records work. If your domain is hosted on Cloudflare, see How to Add a TXT Record in Cloudflare for the exact dashboard steps and common paste errors.