Guide

How to Set Up an SPF Record: The Complete Guide

SPF lists the servers authorised to send email for your domain. One wrong character or one too many includes breaks it silently. This guide covers building, testing, and maintaining your SPF record.

Updated

SPF is a single TXT record that answers: which hosts may use this domain in the SMTP envelope sender (Return-Path / MAIL FROM)? One typo, one duplicate v=spf1 record, or one too many include: lookups — and you get SPF PermError. Receivers treat that as “no valid SPF,” which is worse than a softfail. Our SPF glossary defines the pieces; this page is how you build and keep a record that survives real infrastructure.

What SPF does and why it matters

SPF does not sign message bodies. It authorizes IP-based senders and some indirections via include:, a, mx, and ptr mechanisms (though PTR in SPF is limited and often avoided). Receivers evaluate SPF against the envelope — not the visible From: header. That distinction matters when marketing tools use their own bounce domain: SPF may pass for bounces.example.com while DMARC still requires DKIM alignment to your brand domain. Pair SPF with DKIM and DMARC; see the DMARC guide.

Why operators care: SPF is cheap to deploy, easy to break, and the first thing spam filters check when mail volume spikes. A PermError during a product launch is a bad news story. A correct SPF with ~all buys time while you map every sender.

Building your first SPF record

Start minimal. One mechanism, one fallback. Expand when you add providers.

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

~all is softfail: unlisted senders are marked suspicious but often still delivered. -all is hardfail — use only when every path is known. The literal “all” mechanism must be last. Only one SPF TXT may start with v=spf1 per domain/DNS node — duplicates cause immediate failure.

DNS details: publish at the domain that appears in the envelope sender you control. Subdomain sending is common; you may need SPF on those labels too. Our SPF DNS reference shows host placement patterns.

Adding multiple senders

Merge includes into one record. Example: Google Workspace + SendGrid + a static IP.

v=spf1 include:_spf.google.com include:sendgrid.net ip4:203.0.113.10 ~all

Order does not change semantics, but readability matters for audits. Document each include’s owner. When an ESP changes their include chain, your lookup count can cross ten without you editing characters — re-run a tree regularly. Multi-ESP setups are covered in SPF with multiple providers.

If a vendor asks you to flatten SPF, understand why: they are trying to save lookups. Flattening hardcodes IPs and adds operational risk when those IPs rotate. Prefer removing unused includes or splitting mail across subdomains when possible.

The 10-lookup limit — why it exists

SPF evaluation follows DNS chains. Each include:, a, mx, ptr, and exists consumes lookups. The RFC cap stops unbounded resolver load. Exceeding ten triggers PermError in strict parsers — and many large receivers are strict.

Nested includes count. include:sendgrid.net might pull three more DNS targets. You cannot “see” the count from the outer string; you must expand the tree. That is what DomainPreflight DNS Preflight SPF visualization is for — paste your domain, read the lookup count before go-live.

How to count your lookups

Manual method: recursively resolve each mechanism, count terminal mechanisms, stop at ten. Automated method: use DNS Preflight’s SPF tree and fix the highest-churn includes first. Re-check after any ESP onboarding.

When you are over the limit, remove dead vendors first. Then consolidate sending through fewer IPs or subdomains. Last resort: controlled flattening with monitoring. Deep dive: SPF lookup limit on our blog.

Fixing PermError

Symptoms: authentication headers show permerror, or tools report two SPF TXT strings. Fixes: merge TXT records, fix syntax (double spaces rarely matter; malformed macros do), remove duplicate v=spf1, reduce lookups below ten. Walkthrough: Too many lookups and multiple providers.

Softfail vs hardfail (~all vs -all)

Use ~all until monitoring proves every legitimate sender is covered. Move to -all when DMARC is at enforcement and stray mail is either gone or acceptable. Sudden -all with unknown senders creates false positives. Compare tradeoffs in softfail vs hardfail.

Testing your SPF record

After publish, wait for TTL. Query authoritative servers, not just your laptop cache. Send test mail through each provider and inspect Authentication-Results. Run DNS Preflight on the domain — it surfaces SPF, DMARC, and related issues in one pass.

Red-team tip: test from a non-production subdomain before changing corporate -all. Measure helpdesk impact. Document rollback (previous TXT value) in change management.

Tool: Count SPF lookups, validate syntax, and catch PermError before receivers do.

Run DNS Preflight →

Step by step

Step 1 Mailboxes, marketing, support tools, billing — each may need an include or dedicated envelope domain.
Step 2 Example: v=spf1 include:_spf.google.com include:sendgrid.net ~all — adjust to your stack.
Step 3 Multiple SPF TXT strings at the same owner break SPF; consolidate.
Step 4 Stay under 10 lookups to avoid PermError; trim unused vendors first.
Step 5 Query authoritative NS before declaring victory.
Step 6 Use headers or DNS Preflight to confirm spf=pass on each path.

FAQ

What is an SPF record?

A DNS TXT record that lists which servers are authorised to send email for your domain. Receivers check it to verify your email is legitimate.

How many DNS lookups can SPF use?

Maximum 10. Exceeding this causes SPF PermError — receivers may reject your email. Use DomainPreflight to count your lookups.

Can I have two SPF records?

No. Multiple TXT records starting with v=spf1 cause immediate PermError. Merge all senders into one record.

What does ~all mean?

Softfail — unauthorised senders are marked suspicious but usually delivered. Start with ~all, move to -all once all senders are confirmed.

How do I add multiple email providers to SPF?

Add all includes in a single TXT record: v=spf1 include:_spf.google.com include:sendgrid.net ~all. Watch the 10-lookup limit.