24 June 2026 · DMARC Engine · 7 min read
An SPF record is a single DNS TXT record that lists which servers are allowed to send email using your domain. To set it up you publish one v=spf1 record at your domain's root, list each legitimate sending service, and end with ~all (or -all). This guide walks through it from scratch: inventory your senders, build the record, choose the right fail mode, publish, verify, and keep yourself under the SPF 10 DNS-lookup limit that quietly breaks so many records.
If you already have a record that's misbehaving, see fix a failing SPF record instead. This article assumes you're starting clean.
The short answer
For a domain that sends through Google Workspace and a marketing tool, a complete, correct SPF record looks like this:
v=spf1 include:_spf.google.com include:servers.mcsv.net ~all
Publish that as a TXT record on the root of your domain (host @), wait for DNS to propagate, and verify it with an SPF checker. The rest of this guide explains how to build the right version for your senders and avoid the common traps.
Step 1: Inventory every service that sends as your domain
SPF only works if it authorises all of your legitimate senders. Miss one and that mail starts failing authentication. Before touching DNS, list everything that sends email using an address at your domain:
- Your mailbox provider: Google Workspace, Microsoft 365, Zoho, etc.
- Marketing and newsletter platforms: Mailchimp, Brevo, Klaviyo, HubSpot.
- Transactional / app email: SendGrid, Mailgun, Amazon SES, Postmark.
- Helpdesk and CRM tools: Zendesk, Freshdesk, Salesforce.
- Billing, e-commerce and notification systems: Stripe, Shopify, your ERP.
- Any on-premise mail server or scan-to-email device.
For each one, find the SPF include or IP the vendor publishes in their docs (it's usually titled "SPF setup"). Most modern providers give you an include: mechanism; a few give you raw IPs.
Tip: a DMARC report is the single best way to discover senders you forgot. If DMARC is already running, your aggregate reports will show every source sending as your domain.
Step 2: Understand the building blocks
An SPF record is made of mechanisms. The ones you'll actually use:
v=spf1: the version tag. Every SPF record must start with this, exactly once.include:domain: pulls in another domain's SPF record. This is how you authorise SaaS senders (e.g.include:_spf.google.com).a: authorises the IP(s) in your domain's A/AAAA record.mx: authorises the servers listed in your MX records (useful if your mail server also sends).ip4:/ip6:: authorises a specific address or CIDR range (e.g.ip4:198.51.100.10).all: the catch-all at the end that decides what happens to everything not matched above.
Mechanisms are evaluated left to right, and the first match wins, so order matters for performance but the all at the end is what governs the default outcome.
Step 3: Choose your fail mode: ~all vs -all
The qualifier before all tells receivers how to treat mail from servers you didn't list:
~all(softfail): "this probably isn't legitimate, but accept it and mark it." This is the safe default while you're still discovering senders.-all(hardfail): "this is not authorised; reject or bin it." This is the goal once you're confident every sender is listed.+all: authorises the entire internet to send as you. Never use this.
Start with ~all. Once your DMARC reports show no surprise sources for a couple of weeks, tighten to -all. Note that under DMARC, SPF must also align (the visible From domain must match the SPF-authenticated domain), so a passing SPF check isn't the whole story, but -all is still the correct end state for the record itself.
Step 4: Build your record
Combine v=spf1, your sender mechanisms, and your chosen all. A worked example for a business using Microsoft 365, SendGrid for app email, and a single in-house relay:
v=spf1 include:spf.protection.outlook.com include:sendgrid.net ip4:198.51.100.25 ~all
Rules to keep it valid:
- Exactly one SPF record per domain. You cannot have two
v=spf1TXT records; receivers treat that as apermerrorand SPF stops working entirely. If you add a new sender, edit the existing record; don't publish a second one. - One string, space-separated. Mechanisms are separated by single spaces, all on one logical record.
- No trailing
allduplicates. Only oneallmechanism, at the very end.
Step 5: Publish the TXT record
In your DNS provider's control panel (Cloudflare, GoDaddy, Route 53, etc.):
- Create a new record of type TXT.
- Set the host/name to
@(or your root domain, e.g.example.com; providers differ on how they represent the apex). - Set the value/content to your full record, e.g.
v=spf1 include:spf.protection.outlook.com ~all. Don't wrap it in extra quotes unless your provider's UI requires them. - Leave TTL at the default (3600 is fine), or lower it to 300 temporarily if you expect to iterate.
- Save.
SPF is published only at the domain you send from. If you send from subdomains (e.g. mail.example.com), each sending subdomain needs its own SPF record. SPF is not inherited from the parent domain. See adding a domain for managing multiple zones.
Step 6: Verify it
DNS changes aren't instant; allow up to a few hours for propagation, though it's often minutes. Then confirm the record:
- Run our SPF checker: it parses the record, counts your DNS lookups, and flags syntax errors and
permerrorconditions. - Or from a terminal:
dig +short TXT example.com(ornslookup -type=TXT example.comon Windows). You should see exactly one line beginningv=spf1.
Send a test message to a Gmail address, open the message, choose Show original, and check that SPF shows PASS with your domain.
Step 7: Stay under the 10 DNS-lookup limit
This is the single most common reason SPF silently fails. Per RFC 7208, evaluating your record must trigger no more than 10 DNS lookups. Every include, a, mx, ptr and redirect counts, and each include can pull in nested includes that count too. Big providers are the worst offenders: a single include:spf.protection.outlook.com or include:_spf.google.com can expand into several lookups on its own.
Exceed 10 and receivers return a permerror, which most treat as an outright SPF failure for all your mail, even from legitimate senders. The mechanisms ip4: and ip6: do not cost a lookup, which is the key to the fix.
Flattening
Flattening means resolving your include: mechanisms down to the underlying ip4:/ip6: ranges and publishing those directly, eliminating the lookups. For example, replacing a multi-lookup include: with the IP ranges it resolves to:
v=spf1 ip4:198.51.100.0/24 ip4:203.0.113.0/24 include:_spf.google.com ~all
The catch is that providers change their IP ranges without warning, so a hand-flattened record goes stale and starts failing legitimate mail. The right way to flatten is dynamically: re-resolving the ranges on a schedule and updating the record automatically. Our SPF flattening tool and hosted SPF do exactly this: they keep you under 10 lookups and re-flatten when your providers' IPs move, so the record never goes stale. If you'd rather not flatten, you can also consolidate senders or move some onto subdomains.
Common problems
- Two SPF records →
permerror, SPF fails completely. Merge into one. - More than 10 lookups →
permerror. Flatten or consolidate. - Forgot a sender → that service's mail softfails or hardfails. Add its
include. +allleft in by mistake → anyone can spoof you. Change to~all/-all.- Published on the wrong host → record on
wwwor a random subdomain instead of@. Republish at the root. - Expecting SPF alone to stop spoofing → SPF doesn't protect the visible From address. You need DMARC for that.
Next steps
You now have a valid, verified SPF record under the lookup limit. SPF is one of three pillars; pair it with DKIM and a DMARC policy so receivers can act on failures and you get visibility into who's sending as you. From there, work towards -all on SPF and p=reject on DMARC by following the enforcement journey. If wrangling provider IP ranges and the 10-lookup ceiling sounds like ongoing work, hosted SPF manages it for you automatically.