SPFの10回制限はinclude先まで数える:Node.jsで再帰計測
SPF レコードを書くと「DNS 参照は10回まで」という制限に必ず行き当たる。ここで間違えやすいのは、10回を自分の TXT レコードに並んだ include の数だと思ってしまうことだ。実際には include の先のレコードがさらに include や mx を持っていれば、それも同じ10回の枠から引かれる。レコード単体を眺めて「まだ4回」と思っていても、評価全体では上限を超えていることがある。
この記事では、RFC 7208 が何を数えると決めているかを確認したうえで、include と redirect の先までたどって参照回数を数える小さな Node.js スクリプトを作り、架空のレコードと実在のドメインで数えてみる。
貼り付けたレコードの構文を分解して数えるだけなら SPF / DMARC レコード構文チェック(ハシトシステム) がある。このツールは DNS を引かないので、数えられるのは貼ったレコード自身の分だけである。その先を数えるのがこの記事のスクリプトの役目になる。
RFC 7208 が数えるもの
SPF の仕様 RFC 7208 の 4.6.4 節(DNS Lookup Limits)で確認できた規則を並べる。
| 項目 | 内容 | 出典 |
|---|---|---|
| 数える項目 | include・a・mx・ptr・exists の各メカニズムと redirect 修飾子 |
RFC 7208 4.6.4 |
| 上限 | SPF 評価の間に、上の項目の合計を10に制限しなければならない(MUST)。超えたら permerror |
RFC 7208 4.6.4 |
| 数えない項目 | all・ip4・ip6 と exp 修飾子(exp は評価の後で引かれるので対象外) |
RFC 7208 4.6.4 |
| mx の追加制限 | 1つの mx が引く MX レコードそれぞれについて、A/AAAA の問い合わせは10件まで。超えたら permerror |
RFC 7208 4.6.4、5.4 |
| void lookup | 応答が0件または NXDOMAIN になった参照は2回までに制限すべき(SHOULD)。超えたら permerror |
RFC 7208 4.6.4 |
| redirect と all | レコードのどこかに all があれば redirect は無視しなければならない(MUST) |
RFC 7208 6.1 |
「include 先も数える」点は RFC の「SPF 評価の間に」という書き方から読めるが、Google Workspace のヘルプ「Troubleshoot SPF issues」ははっきり書いている。ネストした参照も上限に数えられ、include したドメインがさらに別のドメインを include していれば、その分も合計に入る、という説明である。ヘルプは上限を超えるとSPFの検証が失敗するとも書いている。
実際に試す
前提は Node.js(ここでは Windows 11 上の v24.11.1)。外部パッケージは使わず、標準の node:dns の resolveTxt で TXT レコードを引く。次のファイルを spf-count.mjs として保存する。
1 | |
架空のレコードで数える(ネット不要)
1 | |
1 | |
example.com のレコードそのものに並んでいる参照は include 2つと mx・a の4つだけである。ところが2つの include の先がそれぞれ4つずつ参照を持っているので、評価全体では12回になり、上限を2回超える。レコード単体を見て「4回だから余裕がある」と判断すると、ここを見落とす。
実在のドメインで数える
引数にドメインを渡すと実際の DNS を引く。2026年10月23日に手元で実行した結果を載せる。SPF レコードは各社が随時変えるので、読者が実行すると数字は変わり得る。
1 | |
1 | |
このときの github.com の SPF レコードには include が8つ並んでいた。include 先のうち _spf.salesforce.com が exists を1つ、sendgrid.net が include:ab.sendgrid.net を1つ持っていて、合計はちょうど10回だった。上限内ではあるが、include 先のどれかが参照を1つ増やせば permerror になる位置にある。自分のレコードを変えていなくても、取り込んでいる送信サービス側の変更で上限を超え得る、というのがこの制限のやっかいなところである。
redirect の例として gmail.com も数えた。レコードは v=spf1 redirect=_spf.google.com で、_spf.google.com 側は ip4・ip6 と ~all だけだったので、合計は1回だった。
1 | |
このスクリプトが数えないもの
手元で確かめる用途に絞ったので、仕様のすべては実装していない。
mxが引く MX ホストごとの A/AAAA 問い合わせ(1つの mx につき10件まで)は調べていない。MX レコードが多いドメインをmxで指しているなら、dns.resolveMxで件数を別に確かめる。existsやincludeのマクロ(%{i}など)は展開しない。existsは SPF レコードではなく A レコードを引くので、ここでは1回として数えて先はたどらない。- void lookup は include 先・redirect 先に SPF が無かった場合だけを数えている。
aやmxの応答が0件だった場合は数えていない。
ハシトシステムのツールの数え方について
SPF / DMARC レコード構文チェック は、貼り付けたレコードを空白で区切り、include・a・mx・ptr・exists・redirect を1回ずつ数える。ip4・ip6・all・exp は数えない。数え方は RFC 7208 4.6.4 の一覧と同じで、8回以上で注意、11回以上で上限超過を出す。
上の github.com のレコードを貼ると、表示は「8 / 10」で注意が出る。スクリプトで include 先まで数えると10回なので、ツールの数字は「この先の分を足す前の下限」として読むのが正しい。ツールのページにも、include の先も参照を消費するので実際の合計はこれより多くなる、と書いてある。
もう1点、ツールはレコードに all があっても redirect= を1回として数える。RFC 7208 6.1 では all があれば redirect は無視されるので、この場合は実際より1回多く出る。安全側にずれるだけなので実害は小さいが、all と redirect を両方書いているならどちらかを消す方がよい。redirect が効いていないことに気づいていない書き方であることが多いからである。
まとめ
- SPF の DNS 参照10回は、include 先・redirect 先のレコードが使う参照も合算する(RFC 7208 4.6.4、Google Workspace ヘルプ)。
- 数えるのは
include・a・mx・ptr・exists・redirect。ip4・ip6・all・expは数えない。 - レコード単体で4回でも、include 先次第で全体は12回になり
permerrorになる。 - 2026年10月23日の実測で
github.comはちょうど10回だった。取り込んでいる送信サービスの変更だけで上限を超え得る。 allがあるとredirectは無視される。
参照した一次情報:
- RFC 7208: Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1(4.6.4 節、5.4 節、6.1 節)
- Troubleshoot SPF issues(Google Workspace 管理者ヘルプ)
- Node.js の dns.resolveTxt(Node.js ドキュメント)
ツール: SPF / DMARC レコード構文チェック(ハシトシステム)。SPF と DMARC の TXT レコードを貼ると、メカニズムとタグを1件ずつ分解して意味を表示し、貼ったレコード自身が使う DNS 参照の回数と、+all や ptr などの危険な指定を指摘する。処理はブラウザ内で完結する。