unmask

docs

JA4 の取得方法、LB / CDN 設定、対応 distro、FAQ。

UA は名札であって、身分証ではない

bot 対策を入れると、必ず検索エンジンの例外が要ります。Googlebot を止めたい人はいません。そして例外の 書き方として真っ先に思いつくのが UA 文字列での判定ですが、これは筋が悪い。 UA は名乗る側が自由に決められるからです。攻撃はこれだけで済みます。

curl -A 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)' https://example.com/

UA のホワイトリストは、合言葉が表に書いてあるドアです。しかも 信頼されているクローラーほど、名前を借りる価値が高い。 通したい相手ほど詐称の圧力が強いという、逆向きの構造になっています。

救いは、その圧力が解決策を生んだことです。詐称する価値のあるクローラーを持つベンダー — Google、Microsoft、OpenAI、Apple など — は クローラーが実際にどの IP range から来るかを公開しています。機械可読な JSON で、 更新も続いています。これで身元は、名乗る側が選べないものになります。

unmask の判定

unmask は公開された range を preset として同梱し、名前ではなくアドレスで照合します。 関係する設定は 2 つで、それぞれが別々の救済経路を持っていると捉えると分かりやすい。

設定 場所 働き
公式 IP range の preset 設定 → ホワイトリスト IP 公開されているリスト 1 本につき preset 1 つ。既定で全部 ON なので、入れた直後から本物のクローラーをアドレスで見分けます。
IP range で検証できる bot は UA を見ず IP だけで判定する 設定 → UA フィルタ 全体方針にあたるスイッチ。公開 range のあるパターンについては UA 文字列が通行証として一切効かなくなり、range 内のアドレスだけが通ります。既定 ON。

どちらも出荷時のままなら、レンタル VPS から来る Googlebot/2.1 は 検索エンジンではありません。ただの正体不明のクライアントとして、通常の challenge に 当たります。本物の Googlebot は Google の公開 range から来るのでこれまでどおり素通りし、何も解かされ ないので検索順位には影響しません。

公開 range の無いクローラーは、UA 文字列の名乗りだけで通ります。その名乗りは誰でも書けるので、 名乗りで得られるものはあえて小さくしてあります。免除されるのはブラウザが受ける challenge だけで、それ以外は 誰とも同じ扱いです。踏んだ honeypot、コミュニティの BAN 一覧、国・ネットワーク・カスタムルール、保護パス、deny、 そしてレート制限はすべて効きます。名乗りが効くのは GET と HEAD だけで、IP ごとの上限 (設定 → UA フィルタ、既定 30 件/分) を超えると CAPTCHA になります。各クローラーの「詳細」には直近 24 時間に名乗りだけで通した件数と送信元 IP 数が出るので、悪用されている名前は「通常判定」か「止める」に 切り替えられます。

SNS のリンクプレビューの取得も名乗りクローラーで、しかも海外から来ます。そのため、世界中のスクレイパー向けに 書いた国ルールは、プレビューの取得も一緒に止めてしまいます (気付くのは画像の出ない投稿が 1 日分たまってから)。 そこで X・Facebook・LINE の取得は、名乗りと送信元のネットワーク (AS13414・AS32934・AS38631。各社自身の AS で、 他者は送信元にできない) の組でも確かめます。両方が合った取得は国・ASN のルールだけ免除され、カスタムルール・ 保護パス・honeypot・レート制限は効いたままです。ASN の mmdb (設定 → ネットワーク) が 要ります。mmdb が無い場合や、クラウドから来る取得 (Slack・Discord など) については、国タブがルールの編集中に 「直近 1 日の通信からみて止まる取得」を示し、止まり始めると通知一覧に出ます。

これは rDNS 方式ではありません。 Googlebot の検証方法として古くから知られているのは 逆引き DNS と正引きの突き合わせです。有効な手ですが、リクエスト経路に DNS の往復が挟まります。 メモリ上の prefix リストとの照合ならリクエストあたりのコストはゼロなので、unmask はリストを同梱する 側を採っています。

この方式で検証できるクローラー

以下のベンダーは機械可読な range を公開しているため、アドレスで身元を判定できます。

ベンダー 対象
Google Googlebot と Google 名義の fetcher 全般 — Image / News / Video、AdsBot、Mediapartners、Inspection Tool、Google-Extended、Read-Aloud、Site-Verification。公開リストは 3 本に分かれていますが、Google が製品をファイル間で移すため unmask は 3 本の和集合で照合します。
Microsoft bingbot、BingPreview
OpenAI GPTBot、OAI-SearchBot、ChatGPT-User — 製品ごとに別リスト
Anthropic (v0.1.30+) ClaudeBot、Claude-User、Claude-SearchBot — 全 crawler 共通の 1 リストです。Anthropic は長らく range を公開していませんでしたが、公開が始まったため対応しました。旧名の Claude-Web / anthropic-ai も意図的に対象です。
Perplexity PerplexityBot、Perplexity-User
DuckDuckGo DuckDuckBot、DuckAssistBot
Apple Applebot、Applebot-Extended
Amazon Amazonbot

終了した製品の名前も、あえて対象に含めています。 Google Web Preview のような すでに実トラフィックのない名前を UA 判定のまま残すと、通るのは詐称だけになります。 「Googlebot は検証しているのに Google Favicon は素通り」では防御になりません。ベンダー名義の パターンが 1 つ開いていれば、そこが穴です。

range を公開していないクローラー — 中小の検索エンジン、フィードリーダー、 リンクプレビュー、自社の監視 bot など — は UA 文字列でしか見分けられないので、そちらが救済 経路として残ります。これは回避のしようがありません。どこから来るかを公開していない相手は、 本物と偽物を区別できないからです。通す価値があるかどうかで判断し、不要なものは 設定 → UA フィルタ の各グループの「詳細」で「通常判定」か「止める」にしてください。

1 つだけ確認しておくとよいこと

2 つの設定は噛み合っている必要があります。そして両者の間には意図的な非対称があるので、 どちらかを変える前に知っておく価値があります。

range の preset を OFF にすると、そのベンダーについては UA 判定が復活します。 これは手落ちではなく安全側の設計です。preset を切ったときに UA 救済も落としてしまうと、 本物のクローラーに通る道が 1 本も残らず、Googlebot を止めることになります。だから unmask は 「UA も拒否、range も OFF」という状態には決して落ちません。裏を返すと、 preset を 1 つ切ると、この方針が消したはずの詐称可能な状態が黙って戻ります。

UA フィルタのタブで「通す」にしたクローラーが例外を作ることはありません。スイッチが ON の間は、 range preset が有効なパターンは「通す」にしていてもアドレスで判定されます。 UA 文字列でも通るようになるのは、スイッチを OFF にしたときだけです。確認すべきなのは preset の側です。 UA フィルタのタブでは range を持つ各行に 🛡 バッジが付き、公開 range がいまホワイトリスト IP タブで 有効なら緑になります。緑でない行は、UA 文字列で通る状態に戻っているベンダーです。

この 2 軸は、ホストの nginx module でも gateway コンテナでも同じように働くので、 導入形態によって結論が変わることはありません。

自分の環境で確かめる

偽の Googlebot を自分で送ってみるのが早いです。ベンダーの range 外にあるマシンなら何でも構いません — 手元のノート PC でも VPS でも。unmask が保護しているパスを叩きます。

curl -sS -o /dev/null -w '%{http_code}\n' \
  -A 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)' \
  https://your-site/<保護しているパス>

challenge ページを伴う 403 が正解です。名前は無視され、アドレスで判定された、ということです。200 が返る場合はそのパターンがまだ UA 文字列で救済されているので、 上の 2 つの設定を確認してください。

逆方向は外からは試せません。そこが肝で、Google の公開 range 内からリクエストを出すことこそ、 詐称する側にできないことだからです。代わりに確認できるのは 本物のクローラーがちゃんと来ているかです。管理画面の AI / crawler traffic カード (ダッシュボードと統計ページ) でクローラーの分類別の内訳が見られ、 分類を開くと個々のクローラーが並び、アドレスで検証済みのものには 🛡 が付きます。Search Console の URL 検査で 「公開 URL をテスト」すれば、本物の range から本物の Googlebot が取りに来ます。

nginx module でどちらかの設定を変えたときは、判定が生成済みの nginx 設定に 載っていることに注意してください。タブを保存するとその場で render し直されますが、動いている nginx は reload するまで前の render のままです (nginx -t の後に reload。gateway コンテナは自動で reload します)。 nginx が古い render のまま動いているときと、render 済みのファイルが config.yml と食い違っているとき (手で編集した後は unmask render-nginx を実行) は、unmask doctor が警告します。