SPF Record Setup: A Complete Guide to Configuring Sender Policy Framework
Step-by-step guide to creating, testing, and troubleshooting SPF DNS records. Covers all mechanisms (ip4, ip6, include, a, mx), qualifiers, the 10-lookup limit, and alignment with DMARC.

SPF Record Setup: A Complete Guide to Configuring Sender Policy Framework
Sender Policy Framework (SPF) is a DNS-based email authentication standard that lets domain owners publish a list of IP addresses and hostnames authorised to send mail on their behalf. When a receiving mail server gets a message, it looks up the SPF record for the sender's domain and checks whether the sending IP appears in that list. A pass result signals that the message is legitimate; a fail or softfail can trigger spam filtering or outright rejection.
SPF is one leg of the three-legged stool of email authentication. The other two are DKIM (which adds a cryptographic signature to the message headers) and DMARC (which ties SPF and DKIM together into a policy and tells receivers what to do with failures). You need all three for reliable inbox delivery, but SPF is usually the first you set up because it requires no cryptographic key management — just a DNS TXT record.
How SPF Works
When you send an email, the receiving server looks at the MAIL FROM address (also called the envelope sender or return-path) to find the domain to check. It then fetches the TXT record at that domain and evaluates the record against the connecting IP address.
The evaluation walks through the record's mechanisms left to right, testing each one against the sending IP. The first mechanism that matches determines the result. If nothing matches, the default result is neutral (effectively no opinion).
Anatomy of an SPF Record
An SPF record is a DNS TXT record at your domain (or subdomain). A typical record looks like:
v=spf1 ip4:203.0.113.0/24 include:mail.rovela.dev include:_spf.google.com ~all
Every record starts with v=spf1. After that come one or more mechanisms and, at the end, a catch-all mechanism.
Mechanisms
ip4 and ip6 are the most straightforward. They match a specific IP address or CIDR range:
ip4:203.0.113.5
ip4:203.0.113.0/24
ip6:2001:db8::/32
Use these for static IPs that your own servers, VPNs, or dedicated relays use to send mail.
include delegates to another domain's SPF record. When the evaluator hits an include, it fetches that domain's SPF record and evaluates it. If the IP matches anything in the included record, the include passes. This is how transactional email providers (like Rovela) let you authorise their sending infrastructure without listing every IP:
include:mail.rovela.dev
a matches the A (and AAAA) records of the current domain or a specified domain. Useful if you send from the same server that hosts your website:
a
a:mail.example.com
mx matches the mail exchange servers listed in your domain's MX records. This is only correct when your inbound and outbound mail servers are the same machines. Many organisations receive mail at Google Workspace or Microsoft 365 while sending through a separate platform; in that setup mx is wrong and silently authorises the wrong hosts.
mx
mx:example.com
exists performs an A-record lookup for a dynamic hostname and passes if any A record is returned. Advanced use case, rarely needed — it shows up in setups that encode the sending source into a subdomain and branch on it.
ptr performs a reverse DNS lookup. Avoid it — it is slow, unreliable, and deprecated by RFC 7208.
Qualifiers
Each mechanism can be prefixed with a qualifier that determines what result to return when the mechanism matches:
| Qualifier | Result | Meaning |
|---|---|---|
+ | Pass | Authorised sender (default, usually omitted) |
- | Fail | Explicitly not authorised; receivers should reject |
~ | SoftFail | Probably not authorised; receivers should accept but mark |
? | Neutral | No policy statement either way |
The catch-all at the end of your record uses one of these qualifiers to cover all IPs that did not match any mechanism:
-all # hard fail: reject mail from unlisted IPs
~all # soft fail: accept but mark as suspect
?all # neutral: no statement
Start with ~all (softfail) while you are auditing your sending sources. Move to -all once you are confident every legitimate sending source is in the record.
The 10-Lookup Limit
This is the most common cause of SPF failures in production. RFC 7208 limits the number of DNS lookups that an SPF evaluation may trigger to ten. Each include, a, mx, and exists mechanism counts as a lookup, as do nested includes inside the included records. Exceeding ten lookups produces a permerror result, which most receivers treat as a fail.
Look at a record that is easy to underestimate:
v=spf1 include:mailprovider1.com include:mailprovider2.com include:mailprovider3.com include:crm.example.com include:support.example.com ~all
…looks like five lookups, but each included domain's record can itself contain several include statements. A single large ESP's SPF record can consume four or five lookups on its own. The failure mode in practice is not "I added too many providers" — it is "I added three, and one of them quietly grew its internal dependencies."
To stay under the limit:
- Audit before adding. Read the current record with
dig TXT yourdomain.com +short, thendigeach included domain and expand the nesting by hand. An online SPF checker will also report the total lookup count directly. - Use
ip4/ip6directly for servers whose IPs are stable. An IP mechanism costs zero lookups, so your own servers and fixed egress addresses should be CIDR blocks, not includes. - Flatten includes if needed — replace
include:provider.comwith the actual IPs from their record. The cost is that the provider can change IPs without telling you, so this suits your own ranges and not cloud platforms. - Use subdomains when you send from several very different systems. Keep transactional mail in
send.example.comand marketing mail innews.example.com, each with their own SPF records, so their lookup budgets don't compete. This is the least fragile option. - Re-check periodically. You don't control anyone else's SPF record, and a provider adding an include once or twice a year is normal. Put it in the quarterly audit rather than waiting for the bounce rate to move.
Setting Up SPF Step by Step
1. Audit your sending sources
Before touching DNS, list every system that sends mail using your domain:
- Your own mail servers (inbound MX servers often relay outbound too)
- Transactional email APIs (Rovela, SendGrid, Postmark, etc.)
- Marketing email platforms (Mailchimp, HubSpot, etc.)
- CRM and support tools
- Internal applications (password resets, notifications, reports)
- Office 365 or Google Workspace, if your team uses them
2. Get the SPF includes from each provider
Every reputable provider publishes their SPF record. Rovela's is include:mail.rovela.dev. For Google Workspace it is include:_spf.google.com. Check each provider's documentation.
3. Write the record
Combine everything into one TXT record at the root of your domain. You can only have one SPF record per domain (one TXT record that begins with v=spf1). Multiple SPF records cause a permerror.
v=spf1 ip4:203.0.113.0/24 include:mail.rovela.dev include:_spf.google.com ~all
4. Add it to DNS
Add a TXT record at @ (or your domain root) with the value above. TTL of 3600 (one hour) is typical. If you are migrating from an existing record, lower the TTL to 300 a day before you change it so the update propagates quickly.
5. Test before going to production
Use dig TXT yourdomain.com +short to confirm the record was picked up. Then use check-auth2@verifier.port25.com — send any email there and you will receive an automated reply showing the SPF result, the DKIM result, and the DMARC result.
Online tools like MXToolbox's SPF checker let you test against a specific IP without actually sending a message.
6. Monitor in DMARC reports
Once you have DMARC reporting set up (even p=none), you will receive aggregate reports showing which IPs are sending mail that claims to be from your domain and whether they are passing SPF. This is how you discover shadow senders you forgot to include.
Common Problems and Fixes
Multiple SPF records. Remove all but one. Combine any authorised sources into the single remaining record.
SPF passes but DMARC fails. DMARC requires the SPF-authenticated domain to align with the From: header domain. SPF checks the envelope sender (return-path), not the From header. If your ESP uses their own return-path domain, SPF alignment will fail; you need DKIM alignment instead. This is normal for most cloud email providers.
Forwarded mail failing SPF. Email forwarding rewrites the routing but usually keeps the original envelope sender, so the forwarding server's IP fails your SPF check. SPF was designed before forwarding was common. DMARC handles this via DKIM: as long as the DKIM signature passes, DMARC will pass even if SPF fails. Enable DKIM to protect forwarded mail.
Softfail landing in spam. ISPs are getting more aggressive about ~all. If your record is complete and stable, switch to -all. The stricter posture paradoxically improves inbox placement because it signals confidence.
Combining SPF with DKIM and DMARC
SPF alone is not enough. It only authenticates the envelope sender, and it breaks on forwarding. The full stack:
- SPF — authorises sending IPs for your domain
- DKIM — adds a cryptographic signature that survives forwarding
- DMARC — uses SPF and DKIM results to enforce a policy and send aggregate reports
A typical production setup for Rovela customers:
; SPF
yourdomain.com. IN TXT "v=spf1 include:mail.rovela.dev ~all"
; DKIM (the key is created in the Rovela dashboard; Rovela handles key rotation)
rovela._domainkey.yourdomain.com. IN TXT "v=DKIM1; k=rsa; p=<public key>"
; DMARC
_dmarc.yourdomain.com. IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com; pct=100"
Rovela's one-click DNS setup handles SPF and DKIM in a single step for Cloudflare and DNSPod customers. Once DNS propagates, your outbound mail will pass all three checks.
Subdomains
SPF does not automatically apply to subdomains. If you send from notifications.yourdomain.com, you need a separate SPF record at that subdomain.
The more important direction is the reverse: a subdomain that will never send mail should carry an explicit v=spf1 -all. With no SPF record at all, anyone can spoof that subdomain with your brand on it, and receivers generally treat "no SPF" as no objection rather than a reason to reject. The record costs nothing:
; subdomains that never send
yourdomain.com. IN TXT "v=spf1 -all"
*.yourdomain.com. IN TXT "v=spf1 -all"
Be careful not to apply -all to a root domain that does send mail — that cuts off every legitimate sender at once.
Troubleshooting Order
When SPF misbehaves, work through this order instead of editing the record on instinct:
- Confirm which record is being read.
dig TXT yourdomain.com +short. If more than one record begins withv=spf1, delete all but one — nothing else matters until that's fixed. - Confirm which domain the receiver checked. SPF evaluates
MAIL FROM, not theFrom:header. Your server logs show the real envelope sender. - Count the lookups. Use a tool; don't eyeball it.
- Confirm the sending IP is actually listed. Compare the real egress IP against the flattened list. An unexpected NAT hop is a common surprise.
- Confirm alignment. SPF passing with DMARC failing is almost always an alignment problem rather than an SPF problem — those are two different checks.
Maintenance
An SPF record is living configuration, not a one-time setting:
- Update it when you add a service. Every SaaS tool that sends on your behalf — CRM, helpdesk, analytics, notifications — belongs in the record the same day. A missing include doesn't raise an error; it just starts losing that mail.
- Audit quarterly. Check that every
includestill resolves, that the IP blocks are still yours, and that the lookup count is under ten. When a provider retires an old domain, your include turns into apermerrorand nothing tells you. - Clean up retired services. An include left behind by a tool you stopped using wastes lookup budget and, worse, keeps a sending authorisation permanently open. Either reason alone justifies removing it.
- Lower the TTL before a big change. Drop from 3600 to 300 so a mistake can be rolled back quickly.
- Watch it alongside DKIM. Maintaining SPF while letting DKIM drift shows up first as a slow decline in DMARC pass rates — visible in aggregate reports well before it shows up in bounce rates.