GLOSSARY

What is SPF?

SPF (Sender Policy Framework, RFC 7208) is a DNS TXT record that lists which hosts are allowed to send mail using your domain in the SMTP envelope sender, so a receiving server can check the connecting IP address against that list and return pass, fail, softfail, neutral, permerror or temperror.

Free forever plan · No credit card required · Cancel anytime

Quick definition

SPF (Sender Policy Framework, RFC 7208) is a DNS TXT record that lists which hosts are allowed to send mail using your domain in the SMTP envelope sender, so a receiving server can check the connecting IP address against that list and return pass, fail, softfail, neutral, permerror or temperror.

In one line: a public list of who is allowed to post letters with your return address on the envelope.

What it means

SPF answers exactly one question: was this IP address allowed to send mail using this domain as the envelope sender? You answer it in advance by publishing a TXT record on the domain, and every receiving server evaluates that record at connection time. The whole standard, RFC 7208, is a small grammar for expressing the sentence "these hosts yes, everything else no".

The word envelope is doing heavy lifting there. SMTP has two layers, and they are routinely different. The envelope is what the two servers say to each other: MAIL FROM:<bounces@pinlyx.com>, which becomes the Return-Path and is where bounces go. The message headers are what the human reads: From: hello@pinlyx.com. SPF only ever looks at the envelope. A phishing message can pass SPF with a perfectly legitimate envelope on a domain the attacker owns while displaying your brand in the From line, and SPF has no opinion about that at all. That gap is the reason DMARC was written.

Reading a record, token by token

A record is a version tag, then an ordered list of mechanisms, each with an optional qualifier. Evaluation stops at the first mechanism that matches, which means order is semantics, not style.

The four qualifiers:

  • + pass. The default, so it is almost always omitted.
  • - fail. A hard no. This is what you want on the final all.
  • ~ softfail. "Probably not authorised, but accept and mark it." A transition state, not a destination.
  • ? neutral. Explicitly no opinion, treated the same as having no record.

There are also two modifiers rather than mechanisms. redirect= replaces the entire evaluation with another domain record, and is only sensible when many domains share one policy. exp= names a TXT record whose text is returned in the rejection message, which is useful for a helpdesk and invisible to everyone else.

The 10-lookup budget, and how teams blow it

This is the part that breaks production. RFC 7208 caps the number of DNS-querying mechanisms evaluated for a single check at ten. include, a, mx, ptr and exists each count, and an include also drags in whatever that record spends. A single large provider include can consume three or four on its own. There is a second, quieter limit of two void lookups, which are queries that return no record.

Cross the limit and the result is permerror. That is not a soft warning: under DMARC a permerror is not a pass, so a domain whose only aligned authentication was SPF starts failing outright, for every message, the moment somebody adds one more vendor. And it can happen without you touching your own record, if a provider expands the record you include.

Four ways out, in order of preference:

  • Delete what you no longer use. Most oversized records contain includes for tools nobody has logged into for years.
  • Split by subdomain. Marketing sends from mail.example.com, the product from notify.example.com, the helpdesk from support.example.com. Each gets its own record with its own budget, and a reputation problem in one does not follow the others.
  • Replace mx and a with literal ip4 and ip6. These cost nothing.
  • Flatten as a last resort. Resolving the includes yourself and pasting the IPs works, but the record then goes stale silently when the provider renumbers, so it needs automation and monitoring, not a one-off copy and paste.

A worked failure

A team sends transactional mail through one provider, campaigns through a second, invoices through an accounting SaaS, and calendar invites through a workspace suite. Each vendor onboarding page says "add this include to your SPF record", so the record ends up with five includes plus an mx. The workspace include alone expands into four lookups. The eleventh lookup pushes the whole evaluation to permerror, and because the campaign vendor uses its own bounce domain, DKIM was the only aligned signal there anyway. Invoices, which were only ever authenticated by SPF, start landing in spam. Nothing in the invoice system changed. The fix was to move invoices to billing.example.com with a two-mechanism record of its own.

What a failure looks like in the logs

SPF problems surface as 5.7.x rejections and as an Authentication-Results header on anything that did get through. Gmail is explicit about it, Outlook less so, and both will usually accept a first offence and start rejecting once the pattern repeats. Whenever you are chasing a deliverability complaint, get a raw message from the recipient and read that header before changing anything.

What SPF does not do

  • It does not stop display-name spoofing. A message from attacker@gmail.com with the display name "Finance Department" passes SPF for gmail.com and always will.
  • It does not survive forwarding. See the FAQ. This is not a bug you can configure around.
  • It does not protect the message body. Nothing in SPF detects modification. That is DKIM.
  • It is not a reputation system. Passing SPF gets you evaluated, not delivered. Volume, complaints and engagement decide the rest.

Related concepts

  • DKIM: the signature-based check that keeps working after a forward.
  • DMARC: the layer that connects an SPF pass to the From address a human reads.
  • Bounce rate: an SPF regression shows up here within hours.
  • Domain warm-up: publishing SPF is step zero of any warm-up plan.
  • Cold outreach: sending from a subdomain with its own SPF record is standard practice.

How Pinlyx handles it

When you attach a sending domain, we resolve the record you publish and count the lookups the way a receiver would, then tell you how many of the ten you have spent before your first campaign goes out. Outbound mail uses a bounce subdomain under your own domain rather than a shared vendor domain, so the SPF pass aligns with the From header and satisfies DMARC on its own. Bounces come back to that subdomain, get classified by enhanced status code, and land on the contact record.

Record anatomy

One good record, one broken one.

The difference is not style. The second one returns permerror on every message.

A record that stays inside the budget

pinlyx.com.   3600 IN TXT "v=spf1 include:_spf.resend.com ip4:46.225.21.115 -all"

; lookups spent: 1 (the include) plus whatever _spf.resend.com costs
; ip4 and -all are free
; -all is last, so nothing after it can be silently ignored

A record that fails every check

example.com.  3600 IN TXT "v=spf1 mx a include:_spf.google.com include:spf.protection.outlook.com include:mail.zendesk.com include:_spf.vendor-a.net include:_spf.vendor-b.io ptr ~all"
example.com.  3600 IN TXT "v=spf1 include:_spf.newvendor.com ~all"

; two v=spf1 records on one name  -> permerror, on its own
; mx + a + five includes + ptr    -> well past the 10 lookup cap
; ptr                             -> RFC 7208 says do not publish it

Two records is the failure mode a vendor setup wizard creates. It is not additive; it is invalid.

What rejection looks like on the wire

550-5.7.26 Your email has been blocked because the sender is unauthenticated.
550-5.7.26 Gmail requires all senders to authenticate with either SPF or DKIM.
550 5.7.26  Authentication results: DKIM = did not pass, SPF [example.com]
550 5.7.26  with ip: [203.0.113.9] = did not pass  - gsmtp

Authentication-Results: mx.google.com;
  spf=permerror (google.com: domain of bounce@example.com uses mechanism
    not recognized by this client) smtp.mailfrom=bounce@example.com;
  dmarc=fail (p=REJECT sp=REJECT dis=REJECT) header.from=example.com
Lookup cost

Every mechanism, and what it costs you.

MechanismDNS lookupsWhat it does
ip4:46.225.21.1150A literal IPv4 address or CIDR block. Costs nothing against the lookup budget, which is why flattening works.
ip6:2a01:4f8::/320The IPv6 equivalent. Omit it and a receiver connecting over IPv6 gets no match at all.
a1The A or AAAA records of the domain itself, or of the domain named after a colon. Rarely what you want for a SaaS.
mx1 plus 1 per MX hostEvery host in your MX records. It authorises your inbound servers to send, which is usually not true and always expensive.
include:_spf.resend.com1, plus whatever that record costsRecursively evaluate another domain policy. This is the mechanism that blows the budget, because you inherit its lookups too.
exists:%{i}._spf.example.com1Macro-expanded existence check. Powerful, almost never needed outside large mail platforms.
ptrmanyReverse DNS matching. RFC 7208 says do not publish or use it. Some receivers ignore it entirely.
all0The catch-all that must come last. Everything after it is ignored, which is a silent way to disable half your record.
Audit in six steps

An SPF record you can defend.

  • Confirm there is exactly one TXT record beginning v=spf1 on the domain. Two is an outage waiting for its trigger.
  • Count the DNS-querying mechanisms including everything your includes expand to, and keep the total at eight or fewer to leave headroom.
  • Add ip6 entries wherever you have ip4 entries. A receiver connecting over IPv6 evaluates a different address family.
  • Remove ptr, and remove mx unless your inbound servers genuinely send outbound mail.
  • Publish v=spf1 -all on every subdomain and parked domain that never sends, so nothing is authorised by omission.
  • End with -all, and verify with DMARC aggregate reports for two weeks before you trust it.

SPF: FAQ

What operators ask after the first deliverability incident.

No. RFC 7208 says a domain must publish exactly one record starting with v=spf1, and a second one is a permerror, which under DMARC counts as a fail. This is the single most common SPF outage: a vendor setup wizard adds its own TXT record instead of editing the existing one, and every message from every sender starts failing at once. Merge the includes into one record and delete the extra.
Evaluating a record may trigger at most 10 DNS-querying mechanisms: include, a, mx, ptr and exists all count, and includes carry their own nested cost. The ip4, ip6 and all mechanisms are free. Exceed 10 and the result is permerror, not fail, but most receivers treat both the same way. Stay under it by dropping unused vendors, replacing mx with explicit ip4 entries, and moving bulk sending to a subdomain with its own record.
End with -all once you are confident the inventory of sending systems is complete. A softfail (~all) tells receivers to accept the message anyway and merely note it, which means an attacker spoofing your domain is still delivered. Use ~all for the first two weeks while DMARC aggregate reports tell you what you forgot, then switch to -all and keep it there.
SPF authorises IP addresses, and a forwarder re-sends from its own IP. Your record does not list that IP, so the check fails at the final destination even though nothing malicious happened. Sender Rewriting Scheme (SRS) fixes it on the forwarder side by rewriting the envelope sender, but you do not control other people forwarders. This is the structural reason DKIM matters more than SPF for DMARC alignment.
No, and this is the most misunderstood part of the standard. SPF checks the envelope sender given in the SMTP MAIL FROM command, which ends up in the Return-Path header and is usually invisible in a mail client. A message can pass SPF with an envelope of bounces@vendor.net while displaying From: ceo@yourbank.com. Closing that gap is precisely why DMARC exists.
No. SPF is looked up on the exact domain in the envelope sender, so mail.example.com with no record of its own has no SPF policy at all. Publish a record on every subdomain that sends, and publish v=spf1 -all on the ones that never should, so a spoofer cannot invent invoices@billing.example.com and pass by default.
Ready to ship

Publish it once. Monitor it forever.

Pinlyx counts your SPF lookups the way a receiver does, sends from a bounce subdomain that aligns with your From address, and classifies every rejection by enhanced status code.

Free forever plan · GDPR-ready · No credit card required

We value your privacy

We use cookies to improve our site, analyze traffic, and personalize ads. You can accept all, reject non-essential, or customize your choices. Read our Cookie Policy.