resellerhq

SPF record parser

Paste a draft SPF record and read it term by term, with the faults named, before anything is published.

Paste the record exactly as you mean to publish it. Nothing is looked up or stored.

Result

Nothing to read yet

Paste a draft SPF record and every term is read as you type.

What this is

This SPF record parser reads a record you paste rather than a domain you own, so a draft can be checked term by term before it is ever published — nothing here is looked up, which is also the one thing it cannot tell you.

An SPF record is a single line of terms read left to right, and the first one that matches the sending server decides the answer. Most of what goes wrong with one is visible in the text alone: a mechanism nobody spelled right, an address with an impossible prefix, a "redirect" sitting beside an "all" that cancels it, or terms added after the "all" where no receiver will ever reach them.

The rest only shows up once the record is live, because it depends on what other people publish: an "include" that costs four lookups instead of one, a target with nothing in it. That half needs a resolver and belongs to the SPF checker; this page is the half you can settle while the record is still in a text box.

How to read the result

The table lists every term in the order a receiver works through them, with what each one authorises and whether it spends a DNS lookup. The lookup column is where a record usually goes wrong later: ip4, ip6 and all cost nothing at all, while each include, a, mx, ptr and exists costs one before its own chain is even followed.

A red finding is a syntax error — the kind that makes a receiver return PermError and ignore the whole record, so it is worse for your mail than the one term you got wrong. An amber one is a term that is valid but does nothing useful where it sits.

Common mistakes

  • Adding a second record instead of editing the one that exists — Each new sender gets its own "v=spf1" line, and now the domain publishes two. A receiver finding more than one has to return PermError, which is a worse outcome than either of the two records would have given on its own.
  • Putting a new include after the "all" at the end — The "all" matches every sender, so evaluation stops there and nothing after it is ever read. The record looks like it lists the new sender, the new sender keeps failing SPF, and nothing in the text is obviously wrong.
  • Writing an address with a prefix the family does not have — An "ip4" term with a /64 on it, or an octet written as 010 because a spreadsheet padded it, is a syntax error rather than a near miss: the receiver stops reading and the record authorises nobody.
  • Trusting a lookup count taken from the text alone — Counting the include terms in a draft gives the floor, not the total. One of them can point at a record holding three more includes of its own, and all four lookups come out of the same budget of ten.

Questions

Does this parser look up the domain or publish anything?

No. It reads the text in the box and makes no DNS query at all, which is why it can check a record that does not exist yet. The trade-off is that it cannot tell you whether this record is the one actually published, or what its includes resolve to.

Why is the DNS lookup count described as an estimate?

Because it assumes every "include", "a" and "mx" resolves exactly once. An include points at somebody else's record, which can hold includes of its own, and those count against the same limit of ten — so the number here is the floor and the real cost can only be higher.

What happens if I leave the "all" term off the end?

A sender matching none of the terms gets the default result, Neutral, under RFC 7208 section 4.7 — which most receivers treat exactly as they treat a domain with no SPF record. The record then only helps the senders you listed and says nothing about anyone else.

Is a "redirect" the same as an "include"?

No. An include asks whether the other record would pass this sender and carries on if it would not; a redirect hands the whole answer over to the other record, including its "all". A redirect is also ignored outright whenever the record has an "all" of its own.

Why is one typo a problem for the whole record rather than just that term?

RFC 7208 treats an unrecognised term as a syntax error in the record, so a receiver returns PermError and ignores every other mechanism in it. A single misspelled "include" leaves every correctly listed server unauthorised.

Sources

Every fixed value this page gives you comes from the provider’s own documentation. These pages change, so each one says the day it was last read here.

Resell mailbox licences for the domains you just checked, under your own brand.

Get started

API example

Example request
curl -X POST https://163-123-236-68.sslip.io/api/tools/spf-record-parser \
  -H "Content-Type: application/json" \
  -d '{"record":"v=spf1 include:_spf.google.com ip4:203.0.113.0/24 ~all"}'
Example response
{
  "tool": "spf-record-parser",
  "input": {
    "record": "v=spf1 include:_spf.google.com ip4:203.0.113.0/24 ~all"
  },
  "result": {
    "verdict": {
      "tone": "success",
      "headline": "This record reads cleanly, at about 1 of 10 lookups",
      "summary": "Every term is one RFC 7208 defines, and the record takes a position on unlisted senders."
    },
    "findings": [
      {
        "id": "lookup-count-is-an-estimate",
        "severity": "info",
        "title": "About 1 of the 10 DNS lookups, as written",
        "detail": "This counts one lookup for each \"include\", \"a\", \"mx\", \"ptr\" and \"exists\" term, and one for a \"redirect\" that is not cancelled by an \"all\" — it is an estimate, not a count. Nothing is published yet to resolve, so a nested include inside one of these can add lookups this page cannot see: 1 is the floor, never the total."
      },
      {
        "id": "record-read-term-by-term",
        "severity": "pass",
        "title": "3 terms read, in order",
        "detail": "Every term of this record is listed below with what it does, in the order a receiver works through them — the first one that matches the sending server decides the result, and the rest are never reached (RFC 7208 §4.6.2)."
      },
      {
        "id": "terminal-all-present",
        "severity": "pass",
        "title": "The record ends with \"~all\"",
        "detail": "A sender matching none of the terms in front of it gets SoftFail, the usual setting while a sender list is still being confirmed — the record takes a position on everyone, which is the point of having one."
      }
    ],
    "parts": [
      {
        "kind": "table",
        "title": "Every term, in the order a receiver reads them",
        "columns": [
          "#",
          "Term",
          "What it does",
          "Lookups"
        ],
        "mono": [
          1
        ],
        "rows": [
          [
            "1",
            "include:_spf.google.com",
            "Also passes whatever _spf.google.com passes, by reading that record too",
            "1"
          ],
          [
            "2",
            "ip4:203.0.113.0/24",
            "Passes 203.0.113.0/24",
            "—"
          ],
          [
            "3",
            "~all",
            "Soft-fails every sender not matched above",
            "—"
          ]
        ],
        "note": "Only the terms marked with a lookup count against the limit of 10; ip4, ip6 and all cost nothing."
      },
      {
        "kind": "facts",
        "title": "The record as a whole",
        "rows": [
          [
            "Terms",
            "3"
          ],
          [
            "DNS lookups, estimated",
            "1 of 10"
          ],
          [
            "Ends with",
            "~all"
          ],
          [
            "Characters",
            "54"
          ]
        ]
      },
      {
        "kind": "record",
        "title": "The record as read",
        "value": "v=spf1 include:_spf.google.com ip4:203.0.113.0/24 ~all",
        "note": "Exactly the first record pasted above, with nothing added or reordered."
      }
    ],
    "limits": [
      "This reads the text you pasted and nothing else. It cannot say whether this record is published anywhere, whether the domain already has another one beside it, or what any of its includes actually resolve to.",
      "The lookup count is an estimate that assumes each \"include\", \"a\" and \"mx\" resolves exactly once. An include whose own record includes two more costs three, not one — run the SPF checker against the domain once this record is published to count what it really spends.",
      "A \"ptr\" mechanism and any macro such as %{i} depend on the sending server's own IP address, which no amount of reading the record can supply."
    ]
  }
}

Related tools

Email authentication

SPF checker

Check an SPF record for the faults that silently break it, and count every DNS lookup it costs.

Email authentication

SPF record generator

Build a valid SPF record from the senders you actually use, and see the DNS-lookup cost before you publish it.

Email authentication

SPF flattening explainer

See how many of a domain’s SPF lookups flattening would actually remove, and what it costs to keep one flattened.

DNS

TXT record splitter

Split a long TXT record value into properly quoted 255-byte character-strings, or the one value a panel that auto-joins would want.