MCMail by MYCLOUD

SPF Explained: How to Set Up an SPF Record for Your Domain

SPF (Sender Policy Framework) tells the world which servers are allowed to send email for your domain. Without it, your messages are more likely to land in spam, and it's easier for scammers to spoof your address. Here's how the record is built, what each mechanism does, and the mistakes that quietly break SPF.

How SPF works

SPF is a TXT record in your domain's DNS. When a receiving server accepts a message, it takes the sender domain from the SMTP MAIL FROM command (also called the envelope sender or Return-Path), looks up that domain's SPF record, and checks whether the connecting server's IP address is on the allowed list.

The result is one of pass, fail, softfail, neutral, or none, plus temperror and permerror for DNS problems or a broken record.

SPF checks the envelope address, not the From address people see in their inbox. DMARC ties the two together through a concept called alignment.

Record syntax

A basic SPF record looks like this:

example.uz.  3600  IN  TXT  "v=spf1 mx ip4:192.0.2.10 include:_spf.crm.example ~all"

It's evaluated left to right until the first match:

  • v=spf1 — required prefix with the protocol version.
  • mx — allow the servers listed in the domain's MX records.
  • ip4:192.0.2.10 — allow a specific IP address.
  • include:_spf.crm.example — allow whatever another domain's SPF record allows (for example, a newsletter service or CRM).
  • ~all — soft fail for everything else.

Mechanisms

MechanismExampleMeaningDNS lookup
ip4ip4:203.0.113.0/24IPv4 address or rangeno
ip6ip6:2001:db8::/32IPv6 address or rangeno
aa or a:mail.example.uzIPs from A/AAAA recordsyes
mxmxIPs of the domain's MX serversyes
includeinclude:_spf.crm.examplePull in another domain's SPFyes
existsexists:%{i}.spf.example.uzAdvanced checks using macrosyes
ptrptrDeprecated; don't use ityes
all-allEverything else; goes lastno

There's also the redirect= modifier, which hands the whole check over to another domain's SPF record if nothing in the current one matched. It's useful when a company has many domains with identical rules.

Qualifiers

Any mechanism can be prefixed with a qualifier that sets the result on a match: + pass (the default, usually omitted), - fail, ~ softfail, ? neutral. In practice, a qualifier is almost always used only on all.

~all or -all?

-all (hard fail) says "anything not listed is definitely not us." ~all (soft fail) says "probably not us, but don't reject outright."

  • With -all, some servers reject the message during the SMTP session, before DKIM and DMARC are even evaluated. If you forgot to list a service, its mail is lost.
  • With ~all, mail from unlisted sources is usually still delivered, but with lower trust or straight to spam.

A sensible approach: start with ~all, identify every real sending source (DMARC reports help here), and then switch to -all if you want. If you run DMARC with a reject policy, DMARC does most of the anti-spoofing work and ~all is perfectly adequate.

Never use +all. It authorizes every server on the internet to send mail as you.

The 10 DNS lookup limit

The SPF standard (RFC 7208) caps the number of mechanisms that trigger DNS lookups at 10 per check. include, a, mx, ptr, exists, and the redirect modifier all count, including nested ones: if your include contains three more includes, that's four lookups. ip4, ip6, and all don't count.

Going over the limit produces a permerror, which many receivers treat as an SPF failure. To stay under it:

  1. Count your lookups. Online SPF checkers, such as the one on mxtoolbox.com, expand nested includes for you.
  2. Remove services you no longer use.
  3. Replace a and mx with explicit ip4 entries if the addresses are stable.
  4. Send newsletters from a subdomain (for example, news.example.uz) with its own SPF record and its own lookup budget.

There's a second, lesser-known limit: no more than two "void" lookups that return no record. It kicks in when your SPF still references domains that no longer exist.

One record per domain

A domain must have exactly one TXT record starting with v=spf1. Two records cause a permerror. This happens a lot: you sign up for a new service, it asks you to "add SPF," and a second record appears next to the first. The fix is to merge them:

Wrong:
"v=spf1 mx ~all"
"v=spf1 include:_spf.crm.example ~all"

Right:
"v=spf1 mx include:_spf.crm.example ~all"

If a record is longer than 255 characters, split it into several quoted strings within the same TXT record; receivers join them without spaces. It's still a single record. The dedicated SPF record type (type 99) is obsolete, so use TXT only.

Other common mistakes

  • Typos: v=spf without the 1, a space after ip4:, an include pointing to a domain that doesn't exist.
  • Mechanisms after all: anything after all is ignored.
  • Forgotten senders: the website's notification emails, accounting software, the CRM, the office scanner.
  • Unused domains without SPF: for domains that never send mail, publish "v=spf1 -all" to make them harder to spoof.
  • Forwarding: a forwarded message leaves from someone else's server, so SPF fails at the final destination. DKIM helps here, because its signature usually survives forwarding.

Check your record with dig TXT example.uz +short or nslookup -type=txt example.uz. For a specific message, look at the Authentication-Results header, for example spf=pass smtp.mailfrom=example.uz.

With MCMail, we set up SPF, DKIM, and DMARC for your domain: we send you the exact records when you connect and help merge them with services you already use. If your messages still land in spam, read why emails go to spam.