Ask what your SPF record authorizes and you get an adjective. Broad. Permissive. A larger attack surface. Ask for a number and the conversation usually stops.

The number is obtainable. It is simply harder to get right than it looks, and almost every way of getting it wrong makes it bigger — which is the direction that sells alarm.

Start with the most widely deployed include there is:

google.com        v=spf1 include:_spf.google.com ~all

_spf.google.com   v=spf1 ip4:74.125.0.0/16 ip4:209.85.128.0/17
                  ip6:2001:4860:4864::/56 ip6:2404:6800:4864::/56
                  ip6:2607:f8b0:4864::/56 ip6:2800:3f0:4864::/56
                  ip6:2a00:1450:4864::/56 ip6:2c0f:fb50:4864::/56 ~all

Resolved August 2026. Provider ranges change without notice — resolve your own rather than trusting any number printed in an article, including this one.

The IPv4 half is school arithmetic. A /16 is 2¹⁶ = 65,536 addresses, a /17 is 32,768. Together: 98,304 addresses entitled to send mail carrying that domain.

Then there is the IPv6 half, and this is where a count that looked easy starts lying. Six blocks. We will come back to them.

That was a simple record — one include, two IPv4 literals, no dynamic mechanisms. Here are the four ways the count goes wrong on everything else.

Rule 1: ip4: and ip6: are not the whole record

Some mechanisms do not carry addresses. They carry instructions for finding addresses, executed at the moment a message arrives.

_spf.salesforce.com   v=spf1 exists:%{i}._spf.mta.salesforce.com -all

Count the ip4: mechanisms in that record: zero. A tool that counts literals will report that this provider authorizes nothing at all — a number that is not merely imprecise but backwards.

What the record actually says is this. exists: builds a domain name out of the connecting IP address (%{i} is the macro for it), looks up an A record at that name, and authorizes the sender if one exists (RFC 7208 §5.7). Authorization is a per-address question, answered by a DNS zone you have no way to list. I tried it with a documentation address — 192.0.2.1._spf.mta.salesforce.com returns NXDOMAIN, so that one is not authorized — but establishing the full set would mean asking the question 4.3 billion times.

a, mx and ptr have the same shape: resolved at evaluation time, against records that can change between one message and the next. (ptr is a special case — RFC 7208 §5.5 is titled “ptr” (do not use) and says the mechanism should not be published. If you find one in your own record, that is a finding in itself.)

So an honest count does two things at once: it totals the literals, and it declares the mechanisms it could not enumerate. What comes out is a floor, never a total. A number that quietly omits the third kind is not a measurement.

Rule 2: IPv4 and IPv6 do not add up

Back to those six IPv6 blocks. Take a bigger example — Microsoft 365’s sending infrastructure, which publishes both families:

spf.protection.outlook.com
  v=spf1 ip4:40.92.0.0/15 ip4:40.107.0.0/16 ip4:52.100.0.0/15
         ip4:52.102.0.0/16 ip4:52.103.0.0/17 ip4:104.47.0.0/17
         ip6:2a01:111:f400::/48 ip6:2a01:111:f403::/49
         ip6:2a01:111:f403:8000::/51 ip6:2a01:111:f403:c000::/51
         ip6:2a01:111:f403:f000::/52 -all

The IPv4 side totals 458,752 addresses. Now the IPv6 side: a single /48 holds 2⁸⁰ addresses, and the five blocks together come to roughly 2.19 × 10²⁴.

Add the two halves and you get 2.19 × 10²⁴ — the same figure. The 458,752 IPv4 addresses do not survive the rounding; they are eighteen orders of magnitude too small to appear. A combined total is not a big number, it is the IPv6 number wearing a disguise, and the part an operator can actually reason about has been erased to produce it.

So: count IPv4 addresses, count IPv6 blocks, and never publish one figure for both. Anyone who tells you your SPF authorizes seventy-nine quintillion addresses has added the two families together and is quoting you the size of IPv6.

Rule 3: overlapping ranges have to be merged first

Three distinct traps live here, and all three inflate.

Ranges that are written separately but are one range. Look again at the Microsoft record above. It publishes six IPv4 literals, but 52.100.0.0/15, 52.102.0.0/16 and 52.103.0.0/17 sit back to back: together they are the single unbroken run 52.100.0.052.103.127.255, 229,376 addresses. There are six literals and four contiguous blocks. Report “six ranges” and you have counted the punctuation rather than the surface.

The same provider counted twice. sendgrid.net already ends with include:ab.sendgrid.net. A domain that publishes both includes — an easy thing to end up with when two setup guides are followed a year apart — inherits that second record along both paths. Sum the literals naively and you get 238,592 addresses. Merge the intervals first and the true figure is 237,056: the difference, 1,536, is exactly the sub-record counted a second time.

Host bits that are not where you think. RFC 7208 §5.6 says only the high-order bits of a prefix are compared, so any bits below the prefix length in the published literal are simply irrelevant. Microsoft’s record for _spf-b.microsoft.com contains ip4:207.46.22.98/29. That does not authorize eight addresses starting at .98. It authorizes 207.46.22.96 through 207.46.22.103 — including .96 and .97, and excluding .104. Take the literal at face value and every interval is misaligned at both ends, which quietly wrecks the merging in the previous paragraph.

Two different figures are at stake here and they are worth keeping apart. Ranges that merely sit next to each other inflate the count of blocks while leaving the address total correct — that is the Microsoft case. A provider inherited twice inflates the addresses themselves — that is the SendGrid one. Misread host bits corrupt both.

What the three have in common is direction. Every one of them makes the figure larger, so a count that skips the merge step is not neutral-but-rough; it is systematically biased towards alarm.

Rule 4: zero and “I don’t know” are different numbers

example.com   v=spf1 -all

That record authorizes exactly zero addresses, and that is a real, useful, defensible fact: it is what a domain that sends no mail should publish.

Now consider an include: that failed to resolve, or a branch abandoned because the record was already at the ten-lookup limit (RFC 7208 §4.6.4). The addresses behind it are not zero. They are unknown, and the honest output is a blank, not a digit.

The distinction sounds pedantic until you notice which way it fails. Rendered as 0, an unknown reads as good news — the most permissive record in your estate, displayed as the tightest. If a tool cannot tell you when it failed to look, its zeros are worthless everywhere else too.

The better question: how many of them have ever sent anything?

The authorized surface is a permissions list, and on its own it says nothing about what is actually in use. The interesting figure is the overlap with reality, and DMARC aggregate reports supply the other half: every report lists the source addresses that sent mail carrying your domain (RFC 9990 §3.1).

Cross the two sets and you get two useful numbers:

  • authorized ∩ observed — the addresses that genuinely send your mail;
  • authorized − observed — the dead weight: addresses you vouch for that have not sent a message in months.

Dead weight is almost always archaeology. The ESP you migrated away from and never removed. A mail server decommissioned two office moves ago. A monitoring appliance that stopped mailing when someone turned off its alerts. None of these announce themselves; the include: keeps resolving, the record keeps validating, and the addresses keep being authorized.

Two disciplines make that number honest, and both matter more than the arithmetic:

No reports is not zero senders. If nothing arrived in the observation window, you have learned nothing about usage. Telling someone who configured reporting last week that none of their 98,304 authorized addresses sends mail would be absurd, and a tool that does it is subtracting from data it does not have. The correct output is silence about usage, not a large and frightening number.

Count distinct sources, not messages. The question is how many authorized addresses are alive, so a server that sent a million messages counts once. Weighting by volume produces a big figure with no relationship to the surface it is supposed to describe.

A wide SPF record is not, by itself, a mistake

This needs saying plainly, because the genre this article belongs to usually ends by implying the opposite.

Google’s 98,304 addresses are not carelessness. They are the size of a mail platform that delivers for millions of organisations, and any provider operating at that scale will authorize a comparable range. Nothing has gone wrong.

Nor is the figure a score. There is no threshold above which a record becomes bad, no benchmark to sit under, and it does not belong behind a red light. It is a measurement — the same category as the number of hosts on your network, which is worth knowing and meaningless as a grade.

It is also worth being precise about what the surface exposes. An authorized address is not an open door: it is a machine whose mail your domain will vouch for. The risk it represents is conditional — it matters if one of those machines is compromised, or if a range is released back to a cloud provider and re-leased to someone else. That is a real concern, and it is exactly why provenance beats size. A record authorizing 98,304 addresses belonging to one provider you actively use is in better shape than one authorizing 3,000 across six vendors, four of which you stopped paying two years ago.

What to actually do with the number

Remove providers you no longer use. This is the one intervention that is always correct, costs nothing, and shrinks both the surface and the lookup count. It requires no judgement call — only the list, and someone willing to say which entries are still real. Start with the dead weight.

Give heavy senders their own subdomain. Marketing and transactional mail sent from mail.example.com, with its own SPF record, keeps the corporate domain’s record small and separately auditable. Your ESP’s ranges then sit behind a name whose only job is bulk mail, and a compromise of that surface does not carry the weight of your primary domain.

Do not reach for flattening. Replacing include:_spf.google.com with the literal ranges it resolves to today does reduce your lookup count, and it is the standard advice for records that have hit the limit of ten. But it converts a live reference into a snapshot, and the provider carries on changing the ranges behind your back. When they add a range, your mail from it starts failing. When they release one — back to the cloud pool, on to another tenant — your record goes on authorizing addresses that now belong to a stranger. That second case is precisely the risk the phrase “attack surface” was invented to describe, and flattening manufactures it. It deserves an article of its own, but the short version is that it trades a limit you can measure for a liability you cannot see.


Counting the surface takes an afternoon and a resolver. Knowing how much of it is still alive takes DMARC reports over a long enough window to catch your quarterly senders, and that is the half no record inspection can give you. Sentinely does both — it measures the authorized surface, declares the parts it could not enumerate rather than rounding them to zero, and crosses the result against the sources your reports actually show, which is where the dead weight becomes visible. If the security framing is what brought you here, that side of it lives here.