What happened
The receiver checked the connecting IP against the sender domain's SPF record and found no match. The record ends in -all (“hardfail”), which effectively says: anything I have not explicitly listed is a forgery. The receiver acted on exactly that.
Important for diagnosis: what gets checked is the IP that actually opens the SMTP connection, and the domain from the envelope-from (return-path) — not the one in the visible From: header. The two can differ.
The four usual causes
- A sender was forgotten. By far the most common case. Newsletter tool, CRM, ticketing system, shop, invoicing or monitoring send from their own IPs that never made it into the record.
- Sending moved to another provider. After a migration the record still points at the old infrastructure.
- IPv6 is missing. The record only contains
ip4:entries while the server delivers over IPv6. To the receiver that is an unknown address. - The mail was forwarded. With classic forwarding the forwarding server sends under the original envelope address — SPF then fails by design. No SPF entry fixes that; ARC exists for it (and SRS on the forwarder's side).
How to proceed
First extract the sending IP from the error or the bounce — it is usually right there, or in the Received: header of the returned message. Then look at your own record and check whether that IP appears in it, directly or via an include:.
If the IP is missing, add it — preferably through the provider's include:, since they maintain their own addresses, and only otherwise as a fixed ip4:/ip6: value.
Watch the lookup limit while extending: more than ten DNS-querying mechanisms invalidate the entire record. That is the second most common way a supposed fix makes things worse.
What not to do
Dropping the record back to ~all or ?all makes the message disappear without solving anything — and weakens your domain's protection against spoofing. When a legitimate source is rejected, that source belongs in the record; the record does not belong watered down.