SPF レコード最適化ガイド — 10 lookup 上限を超えた時の対処法
Google Workspace、Microsoft 365、SendGrid、Amazon SES、Postmark など複数のメール送信サービスを併用しているドメインでは、SPF の DNS ルックアップが 10 回の上限を超えることがあります。RFC 7208(2014年)はこの上限を明確に定めており、超過した時点で受信サーバーは permerror を返します。DMARC はこの permerror を fail として扱うため、p=reject 環境では正規のメールが受信拒否されます。
このガイドでは、現在のルックアップ回数の把握から、不要な include: の削除、ip4:/ip6: への置換、SPF flattening の導入まで、段階的に SPF レコードを最適化する手順を説明します。
なお、一部のメール送信サービスでは SPF の代わりに CNAME ベースの認証を提供しており、include: を追加する必要がないケースもあります。利用中のサービスのセットアップガイドを確認し、CNAME 方式が利用可能であればルックアップ回数の節約になります。
example.com の部分は実際のドメイン名に置き換えてください。
flowchart TD
A["現在の lookup 回数を数える"] --> B{"10 回以下?"}
B -- Yes --> C["対応不要"]
B -- No --> D["使っていない include を削除"]
D --> E{"10 回以下になった?"}
E -- Yes --> F["完了"]
E -- No --> G["ip4/ip6 で置換可能なものを置換"]
G --> H{"10 回以下になった?"}
H -- Yes --> I["完了"]
H -- No --> J["SPF flattening を導入"]
J --> K["定期的な自動更新を設定"]
K --> L["完了"]
チェックリスト一括コピー用
以下をコピーして手元のメモやIssueに貼り付ければ、そのまま確認リストとして使えます。
## SPF レコード最適化チェックリスト
### Step 1 現在のルックアップ回数を数える
- [ ] SPF レコードを dig で取得した
- `dig TXT example.com +short`
- [ ] include / a / mx / ptr / exists / redirect の数を手動で集計した
- [ ] include 先のネスト分も含めた合計ルックアップ回数を把握した
### Step 2 不要な include を削除する
- [ ] 利用中のメール送信サービスを棚卸しした
- [ ] 解約済み・停止中のサービスの include を削除した
- [ ] 削除後のルックアップ回数を再集計した
### Step 3 include を ip4/ip6 に置き換える
- [ ] 固定 IP で運用しているサービスを特定した
- [ ] 該当する include を ip4:/ip6: に書き換えた
- [ ] 書き換え後にメール配信テストを実施し、SPF が pass であることを確認した
### Step 4 SPF flattening を導入する
- [ ] flattening ツールまたはサービスを選定した
- [ ] フラットニング後の SPF レコードを DNS に設定した
- [ ] 定期的な自動更新が機能していることを確認した(手動フラットニングの場合は更新スケジュールを決めた)
### Step 5 redirect による共通ポリシーの検討
- [ ] 複数のサブドメインで同じ送信ポリシーを使っているか確認した
- [ ] 共通ポリシー用のドメイン(例: `_spf-policy.example.com`)に SPF レコードを作成した
- [ ] 各サブドメインの SPF レコードを `v=spf1 redirect=_spf-policy.example.com` に変更した
### Step 6 変更後の検証
- [ ] dig で変更後の SPF レコードを確認した
- `dig TXT example.com +short`
- [ ] ルックアップ回数が 10 回以内に収まっていることを確認した
- [ ] DMARC レポートで spf=permerror が消えたことを確認した
### 一括確認(API)
- `curl "https://labee.dev/api/mail-auth?domain=example.com"`
- `curl "https://labee.dev/api/dns?domain=example.com&type=TXT"`
Step 1 現在のルックアップ回数を数える
SPF レコードの最適化は、まず現状の把握から始まります。ルックアップ回数を正確に数えなければ、どこを削減すべきか判断できません。
DNS ルックアップを伴うメカニズムは include、a、mx、ptr、exists、redirect の 6 種類です。ptr は RFC 7208 で非推奨ですが、レコードに含まれている場合はルックアップとしてカウントされます。ip4:、ip6:、all はルックアップを消費しません。include: は参照先のレコードがさらに include: を含む場合、再帰的にカウントされます。
チェック項目
- SPF レコードを dig で取得した
- レコード内の
include/a/mx/ptr/exists/redirectの数を集計した -
include:先のレコードを再帰的に展開し、ネスト分も含めた合計ルックアップ回数を把握した
確認コマンド
dig TXT example.com +short
"v=spf1 include:_spf.google.com include:sendgrid.net include:amazonses.com ~all"
この例では、トップレベルに include: が 3 個あります。それぞれの内部を展開して数えます。
dig TXT _spf.google.com +short
"v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all"
_spf.google.com だけで 1(自身)+ 3(内部の include)= 4 回を消費します。SendGrid の sendgrid.net は内部に 1 個の include: を持つことが多く 2 回、amazonses.com は 1 回です。合計すると 4 + 2 + 1 = 7 回になります。ここにさらに別のサービスを追加すれば、すぐに 10 回に届きます。
labee.dev の API でも SPF レコードの内容を取得できます。
curl "https://labee.dev/api/mail-auth?domain=example.com"
{
"success": true,
"data": {
"spf": {
"record": "v=spf1 include:_spf.google.com include:sendgrid.net include:amazonses.com ~all",
"exists": true
},
"dkim": {
"mode": "auto",
"record": null,
"exists": false,
"selector": "",
"matches": [],
"checkedSelectors": ["google", "selector1", "selector2", "s1", "s2", "k1", "k2", "k3", "default", "dkim", "mail", "smtp", "mte1", "mte2", "pdk1", "pdk2", "mesmtp", "zoho"],
"exhaustive": false
},
"dmarc": { "record": null, "exists": false, "source": null, "domain": "example.com" },
"bimi": { "record": null, "exists": false }
},
"error": null,
"meta": { "responseTime": 142 }
}
data.spf.record に SPF レコードの全文が返ります。この文字列から include: の数を確認し、各参照先を dig で展開していくことで合計回数を算出できます。
DNS API を使って TXT レコードの生データを取得する方法もあります。
curl "https://labee.dev/api/dns?domain=example.com&type=TXT"
{
"success": true,
"data": {
"domain": "example.com",
"records": {
"TXT": [
["v=spf1 include:_spf.google.com include:sendgrid.net include:amazonses.com ~all"]
]
}
},
"error": null,
"meta": { "responseTime": 85 }
}
data.records.TXT に TXT レコードの配列が返ります。SPF 以外の TXT レコード(ドメイン所有権の検証用トークンなど)も含まれるため、v=spf1 で始まるエントリーを探してください。
Step 2 不要な include を削除する
SPF レコードに残っている include: のうち、すでに解約・停止したサービスのものは無駄にルックアップを消費しています。最も簡単にルックアップ回数を減らす方法は、使っていないサービスの include: を削除することです。
マーケティング部門が過去に導入した配信サービス、トライアルで使った SaaS、組織統合で引き継いだレコードなど、棚卸しすると不要なエントリーが見つかることがあります。
チェック項目
- 現在利用中のメール送信サービスを一覧にした
- SPF レコード内の
include:それぞれについて、対応するサービスがまだ稼働しているか確認した - 解約済み・停止中のサービスに対応する
include:を削除した - 削除後のルックアップ回数を再集計した
確認コマンド
削除後に dig で確認します。
dig TXT example.com +short
"v=spf1 include:_spf.google.com include:sendgrid.net ~all"
include:amazonses.com を削除した結果、ルックアップ回数が 7 回から 6 回に減りました。
labee.dev の API でも削除が反映されていることを確認できます。
curl "https://labee.dev/api/mail-auth?domain=example.com"
{
"success": true,
"data": {
"spf": {
"record": "v=spf1 include:_spf.google.com include:sendgrid.net ~all",
"exists": true
},
"dkim": {
"mode": "auto",
"record": null,
"exists": false,
"selector": "",
"matches": [],
"checkedSelectors": ["google", "selector1", "selector2", "s1", "s2", "k1", "k2", "k3", "default", "dkim", "mail", "smtp", "mte1", "mte2", "pdk1", "pdk2", "mesmtp", "zoho"],
"exhaustive": false
},
"dmarc": { "record": null, "exists": false, "source": null, "domain": "example.com" },
"bimi": { "record": null, "exists": false }
},
"error": null,
"meta": { "responseTime": 128 }
}
data.spf.record の内容が更新されていることを確認してください。DNS の伝播には TTL の時間がかかるため、変更直後は旧レコードが返る場合があります。
Step 3 include を ip4/ip6 に置き換える
include: は参照先のレコードを再帰的にルックアップするためコストが高くなります。送信元 IP が固定されているサービスであれば、include: を ip4: や ip6: に置き換えることでルックアップ回数を 0 回に削減できます。
自社のオンプレミス SMTP サーバーや、IP アドレスが固定の配信サービスが対象になります。Google Workspace や SendGrid のように IP レンジが頻繁に変わるサービスは、手動での ip4: 管理には向いていません(Step 4 で扱う flattening の対象になります)。
チェック項目
- 送信元 IP が固定されているサービスを特定した
- 該当する
include:をip4:またはip6:に書き換えた - 書き換え後にメール配信テストを実施し、SPF が pass であることを確認した
確認コマンド
dig TXT example.com +short
"v=spf1 include:_spf.google.com ip4:149.72.0.0/16 ip4:167.89.0.0/16 ~all"
この例では SendGrid の include:sendgrid.net を ip4: 2 個に置き換えています。ルックアップ回数は include:_spf.google.com の 4 回のみです。
ip4: や ip6: はルックアップを消費しませんが、SPF レコード全体の文字数は増えます。ip4: を大量に列挙してレコードが長くなりすぎると、DNS レスポンスの UDP パケットサイズが推奨上限(1232 バイト、DNS Flag Day 2020)を超え、一部のリゾルバーで切り捨てや TCP フォールバックが発生する場合があります。目安として ip4: が 10 個を超えたら Step 4 の flattening を検討してください。
Step 4 SPF flattening を導入する
SPF flattening は、include: の参照先を再帰的に展開し、最終的な IP アドレスリストに変換する手法です。手動で行うことも可能ですが、Google Workspace や SendGrid は予告なく IP レンジを変更するため、自動更新の仕組みがなければ配信障害の原因になります。
自動フラットニングを提供するサービスがいくつかあります。
- AutoSPF — SPF レコードのマクロを使い、動的に IP を解決する方式
- EasyDMARC SPF Flattening — 定期的にレコードを再生成し、DNS を自動更新する方式
- dmarcian — SPF Surveyor でルックアップ数を可視化し、フラットニングの判断材料を提供
いずれのサービスも、元の include: を定期的に再解決して IP リストを更新し、DNS レコードに反映します。
チェック項目
- フラットニングツールまたはサービスを選定した
- フラットニング後の SPF レコードを DNS に設定した
- 定期的な自動更新が機能していることを確認した(手動フラットニングの場合は更新スケジュールを決めた)
確認コマンド
フラットニング適用後の SPF レコードを確認します。
dig TXT example.com +short
"v=spf1 ip4:209.85.128.0/17 ip4:74.125.0.0/16 ip4:149.72.0.0/16 ip4:167.89.0.0/16 ip4:199.255.192.0/22 ~all"
すべての include: が ip4: に展開されたため、ルックアップ回数は 0 回です。ただし、この IP リストは各サービスの最新の送信 IP レンジと一致している必要があります。手動でフラットニングした場合は、月に1回は各サービスの SPF レコードを dig で確認し、IP の追加・変更がないか照合してください。
Step 5 redirect による共通ポリシーの検討
サブドメイン(mail.example.com、marketing.example.com など)ごとに SPF レコードを管理している場合、redirect= モディファイアーを使って共通の SPF ポリシーを1か所にまとめられます。
redirect= は、SPF 評価を別のドメインのレコードに委譲する仕組みです。include: と異なり、redirect= は元のレコードに他のメカニズム(all を除く)が含まれていない場合にのみ評価されます。ルックアップ回数は 1 回消費します。
チェック項目
- 複数のサブドメインで同じ送信ポリシーを使っているか確認した
- 共通ポリシー用のドメイン(例:
_spf-policy.example.com)に SPF レコードを作成した - 各サブドメインの SPF レコードを
v=spf1 redirect=_spf-policy.example.comに変更した
確認コマンド
dig TXT mail.example.com +short
"v=spf1 redirect=_spf-policy.example.com"
dig TXT _spf-policy.example.com +short
"v=spf1 ip4:209.85.128.0/17 ip4:74.125.0.0/16 ip4:149.72.0.0/16 ~all"
ポリシーの更新は _spf-policy.example.com の1か所だけで済みます。サブドメインごとに異なる送信サービスを使っている場合は、redirect= ではなくサブドメインごとに個別の SPF レコードを設定してください。
Step 6 変更後の検証
SPF レコードの変更後は、ルックアップ回数が 10 回以内に収まっていること、正規のメールが SPF pass になっていることを確認します。DNS の伝播には TTL に応じた時間がかかるため、変更直後ではなく TTL 経過後に検証してください。
チェック項目
- dig で変更後の SPF レコードを確認し、意図した内容になっている
- ルックアップ回数が 10 回以内に収まっている
- テストメールを送信し、受信側の
Authentication-Resultsヘッダーでspf=passを確認した - DMARC レポート(
rua宛の集計レポート)でspf=permerrorが消えたことを確認した
確認コマンド
dig TXT example.com +short
"v=spf1 ip4:209.85.128.0/17 ip4:74.125.0.0/16 ip4:149.72.0.0/16 ip4:167.89.0.0/16 ip4:199.255.192.0/22 ~all"
labee.dev の API で SPF レコードが正しく外部から参照できることを確認します。
curl "https://labee.dev/api/mail-auth?domain=example.com"
{
"success": true,
"data": {
"spf": {
"record": "v=spf1 ip4:209.85.128.0/17 ip4:74.125.0.0/16 ip4:149.72.0.0/16 ip4:167.89.0.0/16 ip4:199.255.192.0/22 ~all",
"exists": true
},
"dkim": {
"mode": "auto",
"record": null,
"exists": false,
"selector": "",
"matches": [],
"checkedSelectors": ["google", "selector1", "selector2", "s1", "s2", "k1", "k2", "k3", "default", "dkim", "mail", "smtp", "mte1", "mte2", "pdk1", "pdk2", "mesmtp", "zoho"],
"exhaustive": false
},
"dmarc": { "record": null, "exists": false, "source": null, "domain": "example.com" },
"bimi": { "record": null, "exists": false }
},
"error": null,
"meta": { "responseTime": 135 }
}
data.spf.exists が true で、data.spf.record の内容が dig の結果と一致していれば、外部からも正しく参照されています。
よくある設定ミス
フラットニング後に IP レンジの更新を忘れる
手動フラットニングで ip4: に書き換えたあと、元のサービスが IP レンジを追加・変更しても SPF レコードには自動的に反映されません。古い IP だけが記載された状態では、新しい IP から送信されたメールが SPF fail になります。手動フラットニングを採用する場合は、月次で各サービスの SPF レコードを dig で確認し、差分があれば更新してください。自動更新サービスを使えばこの作業は不要です。
include を削除したが実はまだ使っていた
棚卸しの際に「もう使っていない」と判断したサービスが、実は社内の別チームや自動通知メールで使われているケースがあります。include: を削除する前に、DMARC の集計レポート(rua)を2週間分確認し、そのサービスの IP からの送信がないことを確認してください。
ip4 に置き換えたのに include も残している
include:sendgrid.net を ip4: に書き換えたにもかかわらず、元の include:sendgrid.net を削除し忘れると、ルックアップ回数は減りません。置き換えの際は必ず元の include: を削除してください。
同一ドメインに SPF レコードを複数作成する
SPF レコードの編集時に、既存のレコードを更新するのではなく新しい TXT レコードを追加してしまうと、同一ドメインに v=spf1 で始まるレコードが2件存在することになります。RFC 7208 の規定により、これは permerror として扱われます。DNS プロバイダーの管理画面で v=spf1 のレコードが 1 件だけであることを必ず確認してください。
void lookup の上限を超える
RFC 7208 は DNS ルックアップの上限 10 回とは別に、結果が返らないルックアップ(void lookup)を 2 回以内に制限しています。存在しないドメインを include: で参照していると void lookup としてカウントされ、2 回を超えた時点で permerror になります。Step 2 の棚卸しで、参照先ドメインが実際に DNS レコードを返すことも合わせて確認してください。
発展的なトピック
SPF レコードの最適化が完了したら、SPF の動作に関連する仕様や仕組みについても理解を深めておくと、今後のトラブルシューティングに役立ちます。
SPF DNS ルックアップ上限
RFC 7208 で定められた 10 回の DNS ルックアップ上限の詳細です。void lookup の 2 回制限も含め、SPF レコード設計の基礎となる仕様です。
用語解説SPF DNS ルックアップ上限(DNS Lookup Limit)1 回の SPF チェックで許可される DNS ルックアップ回数の上限。RFC 7208 で 10 回と定められており、超過すると permerror となる。include メカニズム
include: の動作仕様です。再帰的なルックアップの仕組み、評価順序、fail 時の挙動を理解しておくと、最適化の判断に役立ちます。
redirect モディファイアー
redirect= の動作仕様です。include: との違い、評価条件、ルックアップ消費の仕組みを理解しておくと、共通ポリシーの設計に活用できます。
all メカニズム
SPF レコードの末尾に置く all メカニズムの仕様です。~all(ソフトフェイル)と -all(ハードフェイル)の違い、?all(ニュートラル)の意味を確認できます。
修飾子(Qualifier)
+、-、~、? の 4 種類の修飾子の仕様です。デフォルト(省略時)は +(pass)になります。
TXT レコード
SPF レコードの実体である DNS TXT レコードの仕様です。文字数制限、複数文字列の連結ルール、他の TXT レコードとの共存について確認できます。
用語解説TXT レコードDNS ゾーンに任意のテキスト情報を格納するレコードタイプ。SPF・DKIM・DMARC やドメイン所有権の確認に使われる。