The receiving server looked up the SPF record of the envelope sender’s domain and the sending IP wasn’t in it. Softfail means the record ends in ~all; fail means -all. Paste the header to run the same check for that IP: you see which term decided, whether the IP is in a common sending service’s SPF record, and its reverse DNS name, which often points to a forwarder.
Last verified 9 October 2026.
Paste the message headers (in Gmail: More > Show original), or just the Received-SPF: or spf= line. Or type the envelope sender domain and the IP. The check runs in your browser against public DNS.
Next steps
This evaluates the record as it is in DNS now, for the IP and domain shown. It doesn’t check DKIM or DMARC.
What this can and can’t tell you: it runs RFC 7208’s check_host() for the IP and domain through Cloudflare’s public resolver (Google’s when Cloudflare’s fails): ip4, ip6, a and mx with prefix lengths, include, redirect=, exists with macros, ptr, the 10-lookup and 2-void-lookup limits, and syntax errors. It then checks the IP against the SPF records of common sending services and looks up its reverse DNS name. A lookup that fails shows as temperror, and a macro it can’t fill in (such as %{p}, or %{h} without the HELO name) as “can’t tell here”: never as fail.
When a server receives mail, it runs an SPF check on the domain in the envelope sender (the SMTP MAIL FROM, copied into Return-Path:) and the IP address that connected. It records the answer in the headers. Gmail writes:
Received-SPF: softfail (google.com: domain of transitioning bob@example.com does not designate 192.0.2.1 as permitted sender) client-ip=192.0.2.1;
Authentication-Results: mx.google.com;
spf=softfail (google.com: domain of transitioning bob@example.com does not designate 192.0.2.1 as permitted sender) smtp.mailfrom=bob@example.com
Microsoft writes Received-SPF: SoftFail (protection.outlook.com: domain of transitioning example.com discourages use of 192.0.2.1 as permitted sender), and spf=softfail (sender IP is 192.0.2.1) smtp.mailfrom=example.com in Authentication-Results:. Some receivers leave the IP out of the SPF line; it’s then in the Received: line the receiver added. All of them say the same thing: example.com’s SPF record doesn’t authorize 192.0.2.1.
The last term of the record decides what an unlisted IP gets:
| Record ends in | Result | What RFC 7208 says |
|---|---|---|
~all | softfail | “a weak statement by the publishing ADMD that the host is probably not authorized”; receivers “SHOULD NOT reject the message based solely on this result” (§2.6.5, §8.5). |
-all | fail | “an explicit statement that the client is not authorized to use the domain in the given identity” (§2.6.4). |
?all, or no all and no redirect= | neutral | No statement either way. |
For delivery, DMARC usually matters more than the SPF result alone: a message passes DMARC when SPF or DKIM passes for a domain aligned with the From: address. So a softfail or fail next to an aligned DKIM pass still passes DMARC. A softfail with no passing DKIM fails DMARC, and the domain’s p= policy applies.
Sources: RFC 7208 §2.6.4, §2.6.5, §4.6.4, §5.2, §6.1 and §8.5 (checked 9 October 2026).
The most common cause: you started sending through a new service (a newsletter tool, CRM, help desk, invoicing app, a new mail host) and its servers aren’t listed. Add the include its setup guide gives, before the all term. The checker above tells you when the IP is in one of these services’ records:
| Service | Add |
|---|---|
| Google Workspace | include:_spf.google.com |
| Microsoft 365 | include:spf.protection.outlook.com |
| Amazon SES | include:amazonses.com |
| SendGrid | include:sendgrid.net |
| Mailgun | include:mailgun.org |
| Mailchimp | include:servers.mcsv.net |
| Mailchimp Transactional (Mandrill) | No include: a return-path subdomain with a CNAME to mandrillapp.com |
| Brevo | include:spf.brevo.com |
| Zoho Mail | include:zohomail.com |
| Salesforce | include:_spf.salesforce.com |
| Zendesk | include:mail.zendesk.com |
| Fastmail | include:spf.messagingengine.com |
Amazon SES and Mailchimp Transactional (Mandrill) send with a Return-Path on their own domain by default, so SPF checks their record, not yours. For SES, a custom MAIL FROM subdomain gets its own record, v=spf1 include:amazonses.com ~all, as the SES setup shows. Mandrill’s custom return-path is a subdomain with a CNAME to mandrillapp.com, so it uses Mandrill’s record and needs no include on yours. Each name above publishes an SPF record (checked in DNS on 9 October 2026). Your record stays one TXT record: v=spf1 include:_spf.google.com include:sendgrid.net ~all, never a second v=spf1 record (two records are a permerror).
SPF checks the envelope sender, not the From: address. Look at smtp.mailfrom= or envelope-from= in the header: many services send with a Return-Path on their own domain, or on a subdomain of yours such as bounce.example.com, and that domain’s record is the one checked. Fix the record of the domain the header names, or set up the service’s custom return-path so it uses your domain. For DMARC, the envelope domain also has to align with From:; see DMARC alignment failed.
When an alias, a mailing list or a mailbox forward passes your message on, the forwarder’s server connects to the final receiver with its own IP. Your record can’t list every forwarder, so SPF fails or softfails there, and that’s expected. The fix is DKIM: a signature on your domain survives forwarding as long as the message isn’t modified, and an aligned DKIM pass makes DMARC pass. The checker shows the IP’s reverse DNS name, which often names the forwarder’s network; it can’t prove forwarding on its own.
RFC 7208 §4.6.4 limits include, a, mx, ptr, exists and redirect to 10 DNS lookups per check, and allows at most 2 lookups that find nothing (void lookups). Past either limit the result is permerror, so includes after that point never count. The checker reports the lookup that goes over. See SPF PermError and too many DNS lookups.
An include: of a name without an SPF record doesn’t just fail to match: RFC 7208 §5.2 makes it a permerror, and the same goes for redirect= (§6.1). This happens when a service retires the include name you copied years ago. Replace it with the one the service documents today.
It means the sending IP isn’t in the domain’s SPF record and the record ends in ~all. On its own a softfail rarely gets mail rejected: RFC 7208 says receivers “SHOULD NOT reject the message based solely on this result”. But SPF didn’t pass, so DMARC now depends on DKIM. If DKIM also fails or isn’t aligned, the message fails DMARC and the domain’s policy decides whether it’s junked or rejected.
Only after every service that sends as your domain is in the record. -all asks receivers to treat unlisted IPs as unauthorized, so a service you forgot fails harder. With DMARC at p=quarantine or p=reject, the DMARC policy already decides what happens to unauthenticated mail, so ~all and -all usually make little difference to delivery. One exception: RFC 7489 §10.1 warns that a receiver that checks SPF before DMARC can reject on -all before DMARC is evaluated, even when DKIM would have passed.
SPF checks the envelope sender: the address in MAIL FROM, which ends up in the Return-Path: header. Sending services often set it to their own domain (bounces.example-esp.com), so the header’s smtp.mailfrom= names their domain, not yours. Fixing SPF on your From: domain doesn’t change that result; a custom return-path on your domain, or an aligned DKIM signature, does.
Three usual reasons: the message went out from an IP the service doesn’t list in its own SPF record (a different region or a dedicated IP), the record is over the 10 DNS-lookup limit so receivers return permerror before reaching the include, or a forwarder relayed the message. Paste the header into the checker on this page: it shows each term evaluated for that IP and which one decided.
If you manage domains for clients or brands, Email Security Signals reads each domain’s SPF record with its DNS-lookup count and its all term, alongside DKIM, DMARC, MTA-STS and an A–F grade, one row per domain. $0.02 per graded domain ($20 per 1,000); invalid inputs, domains that don’t exist, DNS lookup failures and duplicates are free.
Related: 550 5.7.23 SPF validation failed, DMARC alignment failed, SPF PermError and the free DMARC, SPF and DKIM checker.