コンテンツへスキップ
ネットワークセキュリティ

AI クローラーはブロックすべきか:判断はボットごとに、実施は IP で

AI クローラーは役割ごとにブロックします。学習ボットは止め、AI 検索ボットは残し、偽の GPTBot には IP を確認して 403 を返します。検証済みの nginx と Express のコード付きです。

著者GeoIPHub チーム公開日読了時間39 分で読めます
検問用のレンズを備えた光の門。1 つのノードが通過し、1 つが止められており、AI クローラーの IP を確認してからブロックを判断することを表しています

アクセスログには GPTBot、ClaudeBot、それに見覚えのない名前がいくつか並んでいます。見つかるガイドはどれも同じことを言います。User-Agent を robots.txt に列挙し、サーバー設定でも同じ文字列をブロックしなさい、と。

この助言には穴があります。User-Agent はクライアントが自分で入力する文字列で、「GPTBot」と書くのに費用はかかりません。名前のブロックリストが止めるのは、どのみち robots.txt に従う誠実なボットだけです。許可した名前を借りるスクレイパーは、すべて素通りします。名前は主張であり、IP が証拠です。これは IP によるボット検出全般と同じ原則です。

以下では、ボットを役割で分類し、robots.txt にできることとできないことを示し、実際のクローラーのアドレス 4 件を当社の API にかけ、検証済みの nginx と Express のコードによる 3 層のポリシーを紹介します。偽の GPTBot には 403 が返り、本物の Googlebot には決して返りません。

半透明の 3 つの層を貫く光線が通過し、そらされ、減速している図。robots.txt、検証済み IP、レート制限という 3 層のクローラーポリシーを表しています
3 つの層:丁寧にお願いし、IP を検証し、残りにレート制限をかけます。

AI クローラーはブロックすべきか:ボットの役割で決まる

異論のある役割の AI クローラーはブロックし、読者を送ってくれるものは残すべきです。AI ボットには 3 種類の役割があり、どれをブロックするかで失うものが異なります。

  • 学習用クローラーは、将来のモデルの学習に使われる可能性のあるページを収集します。OpenAI は、GPTBot を禁止すると「サイトのコンテンツを生成 AI の基盤モデルの学習に使用しないことを示す」と述べています。ブロックしても検索には何の影響もありません。
  • AI 検索ボットは、アシスタントが引用できるようにページをインデックスします。OpenAI は、OAI-SearchBot をオプトアウトしたサイトは「ChatGPT の検索回答に表示されない」と述べています。ブロックすると、引用とそこからのクリックを失います。
  • ユーザー起動型フェッチャーは、ユーザーがアシスタントにページを読むよう頼んだために、そのページを読み込みます。ブロックすると、そのユーザーのリクエストが成立しなくなります。

有料コンテンツを持つ出版社には、学習を拒否する明確な理由があります。アシスタントに推薦されたいブランドは、検索ボットを残すべきです。多くのサイトは結局、学習は拒否、検索は許可、フェッチャーには速度制限、という使い分けに落ち着きます。どの OpenAI ボットをブロックすべきかと、それぞれの選択で何を失うかは、別の記事で扱っています。ユーザーに代わって買い物をするエージェントは別の問題で、決済時の AI ショッピングエージェント不正で扱っています。

学習するボットと、引用してくれるボット

次の表は、よく見かける AI ボットを役割別に整理し、robots.txt についての各運営者の説明を添えたものです。すべてのセルは、2026 年 10 月 20 日に開いた運営者自身のページに基づいています。

ボット運営者役割robots.txt について、運営者の言葉で
GPTBotOpenAI学習Disallow は、コンテンツを「学習に使用すべきでない」ことを意味する
OAI-SearchBotOpenAIAI 検索オプトアウトしたサイトは ChatGPT の検索回答に表示されない
ChatGPT-UserOpenAIユーザー起動型フェッチrobots.txt のルールは「適用されない場合がある」
ClaudeBotAnthropic学習robots.txt と Crawl-delay に従う
Claude-SearchBotAnthropicAI 検索robots.txt と Crawl-delay に従う
Claude-UserAnthropicユーザー起動型フェッチrobots.txt と Crawl-delay に従う
Google-ExtendedGoogleAI 利用のためのトークン。クローラーではないrobots.txt のトークンのみで、独自の User-Agent はない
GooglebotGoogle検索。ブロックすべき AI ボットではない一般的なクローラーは「自動クロール時に常に robots.txt のルールに従う」
PerplexityBotPerplexityAI 検索記載なし。Perplexity は許可を推奨
Perplexity-UserPerplexityユーザー起動型フェッチ「一般に robots.txt のルールを無視する」
CCBotCommon Crawl誰でもダウンロードできるオープンなウェブアーカイブ最初に robots.txt を確認し、Crawl-delay に従う
Applebot-ExtendedAppleAI 学習利用のためのトークン。クローラーではないApple は「ウェブページをクロールしない」としている
Meta-ExternalAgentMeta学習とインデックス記載なし。Meta はこのボット向けの Disallow ルールを文書化している
BytespiderByteDance開けたページでは文書化が見つからなかった開けたページでは文書化が見つからなかった

ポリシーと証明は別の問題です。ボットを検証できるのは、運営者がクロール元を公開している場合だけです。

運営者範囲ファイル2026 年 10 月 20 日時点のプレフィックス数逆引き DNS の文書化GeoIPHub での検証
OpenAIgptbot.json、ほかに searchbot.json と chatgpt-user.json18、39、234記載なしはい、3 つすべて
Anthropicbots.json。3 つのボット共通の 1 ファイル38(ファイル日付 2026 年 10 月 7 日)記載なしはい。一致した場合は ClaudeBot と名付けられる
Googlecommon-crawlers.json とほか 4 つ一般クローラーで 317あり:googlebot.com、google.com、googleusercontent.comはい
Perplexityperplexitybot.json と perplexity-user.json8 と 4記載なしはい
Common Crawlccbot.json5あり。crawl.commoncrawl.org、IPv4 のみはい
Appleapplebot.json24あり。applebot.apple.comはい(Applebot)
Meta公開されていない公開されていない記載なしASN 一致のみ、verified: false
ByteDance見つからなかった見つからなかった記載なしいいえ

他のサイトのリストをコピーする前に、その名前を確認してください。このトピックで上位に出るガイドの中には、Anthropic の現在のページに載っていない anthropic-ai と Claude-Web をいまだに挙げ、Claude-SearchBot と Claude-User を見落としているものがあります。古いリストは、誰も送ってこない文字列をブロックするだけです。

robots.txt にできること、できないこと

robots.txt は、行儀のよいボットに望みを伝えることはできますが、何も止められません。それを定義する標準である RFC 9309 は、「これらのルールはアクセス認可の一形態ではない」と述べています。無視した場合に法的な結果があるかどうかは、弁護士に確認すべき問題です。

それでも第 1 層です。名前の分かっているボットのほとんどが読むからです。次のファイルは、学習は拒否し、AI 検索は歓迎し、Google 検索には手を触れません。

GeoIPHub API
# Training: no User-agent: GPTBot User-agent: ClaudeBot User-agent: CCBot User-agent: Google-Extended User-agent: Applebot-Extended User-agent: Meta-ExternalAgent User-agent: Bytespider Disallow: / # AI search: yes User-agent: OAI-SearchBot User-agent: PerplexityBot Allow: / # Anthropic honours Crawl-delay (seconds between requests) User-agent: Claude-SearchBot Allow: / Crawl-delay: 2

このファイルが思ったとおりに動くかどうかは、4 つの点で決まります。

  • Google-Extended と Applebot-Extended はトークンであり、クローラーではありません。 Google の一般的なクローラーのページ(2026 年 10 月 15 日更新)には、「Google-Extended には独立した HTTP リクエストのユーザーエージェント文字列はない」「Google 検索へのサイトの掲載には影響しない」と書かれています。Google-Extended からのリクエストは一切届かないので、IP のルールでブロックすることはできません。Apple の Applebot のページも、Applebot-Extended は「ウェブページをクロールしない」としています。
  • AI の利用を止めるために Googlebot をブロックすると、検索を失います。 User-agent: * グループに Disallow: / を書いた場合も同じです。
  • ユーザー起動型フェッチャーはこのファイルを無視することがあります。 OpenAI は ChatGPT-User について「robots.txt のルールは適用されない場合がある」と述べています。Perplexity は Perplexity-User が「一般に robots.txt のルールを無視する」と述べています。
  • 変更はすぐには反映されません。 OpenAI は、「サイトの robots.txt が更新されてから当社のシステムが調整するまでに、約 24 時間かかることがある」と述べています。

llms.txt や ai.txt といった新しいファイルは提案段階です。robots.txt と同じく、お願いするだけで強制はしません。

その逆の間違いは、検索クローラーを「AI ボット」のブロックリストに入れてしまうことです。Contentstack のAI クローラーのブロックガイドには、ボットのリストに gptbot と claudebot と並んで googlebot と bingbot を含むエッジ関数が載っており、そのすべてに「Forbidden: AI crawlers are not allowed.」と応答します。書かれたとおりにコピーすると、サイトは Google と Bing の検索から外れます。Googlebot を装う者は偽の Googlebot で、これは別の問題で、対処法も別です。

本物のクローラーか偽物か:検証の仕組み

本物のクローラーと偽物は、接続元の IP を、名乗っている運営者が公開している情報と照合して見分けます。3 つの方法のうち、どれが使えるかは運営者によって異なります。

  • 範囲ファイル。 OpenAI、Anthropic、Perplexity、Common Crawl、Apple、Google は、プレフィックスの JSON リストを公開しています。OpenAI と Anthropic のアドレスには逆引き DNS がないため、これが唯一の方法です。
  • 正引き確認付きの逆引き DNS。 Google の検証ガイドでは、IP を名前に解決し、ドメインを確認し、その名前を再び解決して同じ IP が返ることを確かめます。Apple と Common Crawl も同じ方法を文書化しています。
  • 署名付きリクエスト(Web Bot Auth)。 ボットが公開鍵に対応する鍵で各リクエストに署名します。まだドラフト段階で、下のエッジの節で扱います。

IP による AI クローラーの検証のガイドで、この 3 つを詳しく扱っています。2026 年 10 月 20 日の 04:37 UTC に、キーなしでクローラーのアドレス 4 件を調べました。次は ClaudeBot のアドレスで、一部を省略しています。ネスト構造は保持し、値は編集していません。

GeoIPHub API
{ "ip": "216.73.216.10", "asn": { "asn": 16509, "org": "AMAZON-02 - Amazon.com, Inc." }, "threat": { "is_crawler": true, "crawler": { "verified": true, "name": "ClaudeBot", "operator": "Anthropic", "category": "ai", "verification_method": "ip_range_list" } }, "scoring": { "fraud_score": 0, "recommended_action": "allow", "detection_methods": ["datacenter_ip", "no_ptr_datacenter", "verified_crawler"] } }

同じ実行で得た残りの 3 件です。

IPcrawler.name / 運営者 / カテゴリ検証済み、方法スコア、アクションdetection_methodsASN
132.196.86.1GPTBot / OpenAI / aitrue、ip_range_list0、allowdatacenter_ip, no_ptr_datacenter, verified_crawler8075 MICROSOFT-CORP-MSN-AS-BLOCK
66.249.66.1Googlebot / Google / search_enginetrue、ip_range_list0、allowdatacenter_ip, verified_crawler15169 GOOGLE
107.178.192.1GoogleUserTriggeredFetcher / Google / fetchertrue、ip_range_list0、allowdatacenter_ip, rdns_hosting, verified_crawler396982 GOOGLE-CLOUD-PLATFORM

GPTBot のアドレスは、OpenAI の gptbot.json の最初のプレフィックスである 132.196.86.0/24 の最初のホストです。そのうえで、04:45 UTC に DNS で名前を引きました。

GeoIPHub API
$ dig -x 216.73.216.10 +short # ClaudeBot: NXDOMAIN, no name $ dig -x 132.196.86.1 +short # GPTBot: NXDOMAIN, no name $ dig -x 66.249.66.1 +short crawl-66-249-66-1.googlebot.com. $ dig crawl-66-249-66-1.googlebot.com +short 66.249.66.1

これが、ほとんどのガイドが見落としている発見です。ClaudeBot と GPTBot には PTR レコードがないため、逆引き DNS では検証できません。範囲ファイルが唯一の確認手段です。no_ptr_datacenter は両方で発火しますが、verified_crawler が免責シグナルなので、スコアは 0 のままです。これらの運営者では、PTR がないのが通常です。Googlebot は両方向で通ります。その名前が同じアドレスに解決し直されるからです。

制約が 2 つあります。category フィールドは、学習用か否かにかかわらず AI ボットすべてに "ai" と返すので、ポリシーのコードは crawler.name で判断すべきです。そしてルックアップが見るのは IP だけで User-Agent は見ないため、API が偽装にフラグを立てることはありません。名乗った名前と証明された名前の比較は、お客様のコードで行います。同じ確認は、当社の IP ルックアップツールで手作業でも実行できます。

3 層のポリシー:お願いし、検証し、絞る

しっかり機能するポリシーは 3 層です。robots.txt でお願いし、ボットの主張をすべて IP と照合して検証し、残りにはレート制限をかけます。ブロックは役割で決め、実施は IP で行い、Googlebot を AI のブロックリストに入れてはいけません。

第 1 層:お願いする。 robots.txt に、ボットごとの判断を記し、従う運営者には Crawl-delay も加えます。

第 2 層:検証する。 User-Agent がボットを名乗ったら、接続元の IP を調べて名前を比較します。IP がそのボットのものでなければ、リクエストには 403 を返します。そのボットのものであれば、第 1 層の判断を適用します。したがって、検証済みでも Disallow を無視する GPTBot は、やはり拒否されます。

第 3 層:絞る。 それ以外のすべてにレート制限をかけます。ボットを名乗らないリクエスト、検証できないボット、人に代わって動くフェッチャーです。

ケース例アクション
許可する検証済みボット66.249.66.1 からの Googlebot通常どおり応答し、レート制限なし
拒否した検証済みボットが、なお届くGPTBot を禁止している状態で 132.196.86.1 から来た GPTBot403
未検証の主張:UA はボットを名乗るが、IP が一致しないAnthropic のアドレスである 216.73.216.10 からの GPTBot UA403。好みに応じてチャレンジ
検証できないボットBytespider、Meta-ExternalAgentレート制限。robots.txt が唯一の手段
ボットの主張なしブラウザ、およびブラウザを装うスクレイパーIP ごとにレート制限

3 行目が最も多くを教えてくれます。216.73.216.10 は検証済みのクローラーですが、リクエストが名乗っているクローラーではありません。「これは検証済みのクローラーか」だけを問うチェックでは、通してしまいます。名前を比較してください。同様に、107.178.192.1 は検証済みの Google フェッチャーですが、そこから来て Googlebot を名乗るリクエストは Googlebot ではありません。

第 2 層の失敗はすべて記録してください。公開された範囲の外のアドレスはたいてい偽装ですが、運営者が追加したばかりのプレフィックスかもしれません。当社の範囲ファイルは毎日再取得され、ルックアップデータベースも毎日再構築されるため、新しいプレフィックスの検証には最大で約 2 日かかることがあります。

nginx と Express で実施する:偽の GPTBot でテストする

実施は、ルックアップを行う小さな Express サービス 1 つと、ボットを名乗るすべてのリクエストについてそのサービスに問い合わせる nginx の設定でできます。標準の nginx は HTTP API をインラインで呼び出せないため、auth_request モジュールが検証サービスにサブリクエストを送り、nginx がその応答をキャッシュします。nginx がなくても、同じファイルだけで Express アプリを保護できます。

検証サービスは、User-Agent を現在のボットトークンと照合し、GET https://api.geoiphub.com/v1/lookup/{ip} と x-api-key ヘッダーで IP を調べ、結果を 1 時間キャッシュし、threat.crawler.name を名乗られた名前と比較します。API が一部の名前をハイフンなしで返すため、名前のマップが必要です。OAISearchBot、ChatGPTUser、PerplexityUser がそうです。API ドキュメントのクローラーフィールドを参照してください。

GeoIPHub API
// bot-gate.js: tier 2 (verify the IP behind a bot claim) and tier 3 (rate-limit the rest). // Tested with Node 24.20.0, Express 5.3.0 and express-rate-limit 8.7.1. const express = require('express'); const { rateLimit } = require('express-rate-limit'); const { BlockList, isIP } = require('node:net'); const API_KEY = process.env.GEOIPHUB_KEY; // production: always set a key const CACHE_MS = 60 * 60 * 1000; // one hour per IP const cache = new Map(); // ip: { at, crawler } // Your policy: User-Agent tokens you refuse even when verified. Everything verified and not listed passes. const BLOCKED = new Set((process.env.BLOCKED_BOTS || 'GPTBot,ClaudeBot,CCBot').split(',')); // Maps each User-Agent token to the crawler.name our API returns for that operator's range file. // Anthropic publishes one file for all three of its bots, so a match comes back as ClaudeBot. const EXPECTED = { 'GPTBot': 'GPTBot', 'OAI-SearchBot': 'OAISearchBot', 'ChatGPT-User': 'ChatGPTUser', 'ClaudeBot': 'ClaudeBot', 'Claude-User': 'ClaudeBot', 'Claude-SearchBot': 'ClaudeBot', 'PerplexityBot': 'PerplexityBot', 'Perplexity-User': 'PerplexityUser', 'CCBot': 'CCBot', 'Googlebot': 'Googlebot', 'bingbot': 'Bingbot', 'Applebot': 'Applebot', }; // Longest tokens first, so "Claude-SearchBot" is not read as a shorter token. const TOKENS = Object.keys(EXPECTED).sort((a, b) => b.length - a.length); // A real crawler never connects from a private or loopback address. const privateNets = new BlockList(); for (const [net, bits] of [['10.0.0.0', 8], ['172.16.0.0', 12], ['192.168.0.0', 16], ['127.0.0.0', 8], ['100.64.0.0', 10]]) privateNets.addSubnet(net, bits, 'ipv4'); privateNets.addSubnet('fc00::', 7, 'ipv6'); privateNets.addAddress('::1', 'ipv6'); function claimedBot(ua = '') { const lower = ua.toLowerCase(); return TOKENS.find((t) => lower.includes(t.toLowerCase())) || null; } async function lookupCrawler(ip) { const hit = cache.get(ip); if (hit && Date.now() - hit.at < CACHE_MS) return hit.crawler; const res = await fetch(`https://api.geoiphub.com/v1/lookup/${ip}`, { headers: API_KEY ? { 'x-api-key': API_KEY } : {}, // keyless only for a local test signal: AbortSignal.timeout(1500), }); if (!res.ok) throw new Error(`lookup ${res.status}`); const data = await res.json(); cache.set(ip, { at: Date.now(), crawler: data.threat.crawler }); return data.threat.crawler; } async function verifyBot(req, res, next) { const claim = claimedBot(req.get('user-agent')); if (!claim) return next(); // no bot claim: tier 3 handles it const ip = req.ip.replace(/^::ffff:/, ''); const family = isIP(ip) === 6 ? 'ipv6' : 'ipv4'; if (privateNets.check(ip, family)) { return res.status(403).send(`unverified ${claim} claim\n`); } let crawler; try { crawler = await lookupCrawler(ip); } catch (err) { // Fail mode: never fail open to "verified". Mark it and let the rate limiter decide. req.botUnverified = true; return next(); } // The API never flags a spoof: it sees only the IP, not the User-Agent. // The spoof check is this comparison, in your code. const verified = crawler.verified === true && crawler.name === EXPECTED[claim]; if (!verified) return res.status(403).send(`unverified ${claim} claim\n`); if (BLOCKED.has(claim)) return res.status(403).send(`${claim} is blocked by policy\n`); req.verifiedBot = crawler.name; return next(); } // Tier 3: everyone without a verified bot name, keyed per IPv4 address or per IPv6 /64. // nginx auth_request understands only 2xx, 401 and 403, so the verifier endpoint answers 403. const limiter = rateLimit({ windowMs: 60 * 1000, limit: 120, ipv6Subnet: 64, skip: (req) => Boolean(req.verifiedBot), handler: (req, res) => res.set('Cache-Control', 'no-store') .status(req.path === '/_verify_bot' ? 403 : 429).send('rate limited\n'), }); const app = express(); app.set('trust proxy', 'loopback'); // only nginx on this host may set X-Forwarded-For // Endpoint for nginx auth_request: 204 lets the request through, 403 stops it. // After a failed lookup, no-store keeps nginx from caching the 204, so the limiter sees every request. app.get('/_verify_bot', verifyBot, limiter, (req, res) => { if (req.botUnverified) res.set('Cache-Control', 'no-store'); res.sendStatus(204); }); // Standalone use without nginx: protect every route. app.use(verifyBot, limiter); app.get('/', (req, res) => res.send('ok\n')); app.listen(3000, '127.0.0.1');

意図的な選択が 3 つあります。ルックアップが失敗するか 1.5 秒を超えた場合、そのリクエストは検証済みとして扱われず、レート制限に回されます。nginx の背後では、その応答は no-store 付きで送られるため nginx がキャッシュせず、検証サービスはすべてのリクエストを同じ制限に数えます。ポリシーのリストには User-Agent トークンを入れているので、ClaudeBot を拒否しても Claude-SearchBot は拒否されません。どちらもルックアップ結果では ClaudeBot と返るにもかかわらずです。trust proxy を loopback のみにしているのは、そうしないとクライアントが自分で X-Forwarded-For を送り、確認されるアドレスを選べてしまうからです。そしてボットの主張だけがルックアップ対象なので、件数はページビューではなくボットのトラフィックに比例します。無料プランは、キーがあれば全フィールド込みで 1 日 1,500 回のルックアップをカバーします。

nginx 側では、User-Agent を名乗られたボットにマップし、主張がなければサブリクエストを省略し、検証サービスの 204 または 403 をアドレスとボットごとに 1 時間キャッシュします。ただし no-store が付いた応答は除きます。

GeoIPHub API
# Goes in the http {} block. Tested with nginx 1.30.6 built with --with-http_auth_request_module. # Which bot does the User-Agent claim? Empty means no claim. map $http_user_agent $claimed_bot { default ""; "~*(GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|Claude-User|Claude-SearchBot|PerplexityBot|Perplexity-User|CCBot|Googlebot|bingbot|Applebot)" $1; } # Tier 3 key: rate-limit requests with no bot claim. Claims go to tier 2 instead. map $claimed_bot $limit_key { "" $binary_remote_addr; default ""; } limit_req_zone $limit_key zone=perip:10m rate=10r/s; # Cache the verifier's answer per address and claimed bot for one hour. proxy_cache_path /var/cache/nginx/botcheck keys_zone=botcheck:1m max_size=10m inactive=1h; server { listen 8080; location / { limit_req zone=perip burst=20 nodelay; limit_req_status 429; auth_request /_verify_bot; proxy_pass http://127.0.0.1:8000; # your app } location = /_verify_bot { internal; if ($claimed_bot = "") { return 204; } # no claim: no lookup proxy_pass http://127.0.0.1:3000; # bot-gate.js proxy_pass_request_body off; proxy_set_header Content-Length ""; proxy_set_header X-Forwarded-For $remote_addr; proxy_cache botcheck; proxy_cache_key $remote_addr$claimed_bot; proxy_cache_valid 204 403 1h; } }

この設定で nginx -t は成功し、2026 年 10 月 20 日の 05:05 UTC に、Express のファイルの前段に置いて実行しました。最初は偽の GPTBot のテストで、同じマシンから nginx 経由で行いました。

GeoIPHub API
$ curl -s -o /dev/null -w "%{http_code}\n" \ -A "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; GPTBot/1.4; +https://openai.com/gptbot" \ http://localhost:8080/ 403 $ curl -s -o /dev/null -w "%{http_code}\n" \ -A "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36" \ http://localhost:8080/ 200

この GPTBot の文字列は、OpenAI のボットのページにある現在の例です。お手元のマシンからだと、リクエストは 127.0.0.1 から来ることになり、これはどのクローラーも使わないアドレスなので、検証サービスはルックアップを行う前に拒否します。ルックアップの動作を見るには、nginx が転送するはずのアドレスを指定して、Express のファイルを直接呼び出します。

GeoIPHub API
$ GPT='Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; GPTBot/1.4; +https://openai.com/gptbot' $ GB='Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)' $ curl -s -A "$GPT" -H "X-Forwarded-For: 216.73.216.10" http://127.0.0.1:3000/ unverified GPTBot claim $ curl -s -A "$GPT" -H "X-Forwarded-For: 132.196.86.1" http://127.0.0.1:3000/ GPTBot is blocked by policy $ curl -s -A "$GB" -H "X-Forwarded-For: 107.178.192.1" http://127.0.0.1:3000/ unverified Googlebot claim $ curl -s -A "$GB" -H "X-Forwarded-For: 66.249.66.1" http://127.0.0.1:3000/ ok

1 行目を最初から最後まで追ってみましょう。User-Agent は GPTBot を名乗っています。検証サービスが 216.73.216.10 を調べると、API は ClaudeBot という名前の検証済みクローラーだと答えます。ClaudeBot は GPTBot ではないので、リクエストには 403 が返ります。本物の GPTBot のアドレスである 132.196.86.1 は検証を通り、このポリシーが学習を拒否しているために拒否されるだけです。BLOCKED_BOTS=CCBot にすると 200 が返りました。216.73.216.10 からの Claude-SearchBot の主張は、このポリシーが拒否するのは ClaudeBot トークンであって Anthropic の検索ボットではないため、200 でした。同じ 4 つのアドレスを転送するテスト専用の nginx 設定のコピーでは、403、403、403、200 が返りました。API を意図的に到達不能にした状態で、1 つのアドレスから nginx 経由で bingbot の主張を 130 回送ると、120 回はステータス 200、その後は 403 でした。障害時にはボットの主張を素通しにするのではなく、絞り込みます。

キーなしでテストする場合は、短く済ませてください。匿名のルックアップには 1 分あたりの制限があり、60 秒の間の 30 回目の呼び出しは、すでに 429 になって IP が 2 時間ロックアウトされます。

検証できないものはブロックせず、レート制限をかける

レート制限は、どちらとも証明できないトラフィックに適した手段です。検証できないボット、ユーザー起動型フェッチャー、ボットを名乗らないものすべてがそれに当たります。どのボットが範囲ファイルを公開しているかは、ファイルごとにまとめた当社のリストで確認してください。制限をかければ、人の代わりに動くフェッチャーの背後にいる人々を締め出すことなく、午後のうちにサイトを丸ごとコピーするスクレイパーを止められます。

2 つのスニペットは、すでにこれを行っています。nginx の limit_req は、ボットを名乗らないリクエストについてクライアントのアドレスをキーに、毎秒 10 件、バースト 20 で制限します。当社のテストでは、連続 40 リクエストで 25 件が成功し、15 件がステータス 429 でした。数値は調整してください。1 回のページ読み込みで数十件を取得する場合は、静的アセットを除外してください。Express のリミッターは、IPv6 クライアントを /64 でキー化します。1 人の契約者が通常 /64 全体を管理しているからです。/56 や /48 に広げる場合は、/64 プレフィックスによる IPv6 のレート制限を参照してください。

Crawl-delay は、従うボットに対する丁寧な方法です。Anthropic のクローラーに関するヘルプページには「robots.txt の非標準の Crawl-delay 拡張をサポートしている」とあり、Common Crawl の FAQ には「Crawl-delay パラメーターに従う」とあります。従わないところもあります。Google の robots.txt 仕様は、「crawl-delay などの」フィールドは「サポートされない」と述べ、Apple は「Applebot は crawl-delay に従わない」と述べています。OpenAI と Perplexity のページには、これについての記載がありません。

第 2 層で捕捉できるのは、ボットを名乗るリクエストだけです。ボットを名乗らない AI スクレイパーをブロックするには、第 3 層に振る舞いを加えます。リクエストの頻度、パス、リクエスト元のネットワークです。これらのシグナルは、ログからウェブスクレイピングを検出する方法の記事で扱っています。

エッジの選択肢:Cloudflare の AI ブロックと pay per crawl

Cloudflare の背後にいるなら、コードの代わりに設定で AI クローラーをブロックしたり、リクエストごとに課金したりできます。どちらも、相手がどのボットかを知る必要をなくすものではありません。

Cloudflare は 2025 年 7 月 1 日に、投稿(最終更新 2026 年 7 月 15 日)で、「AI クローラーがコンテンツに対して作成者に支払わない限り、デフォルトでブロックするよう変更する」と発表しました。独自のルールも運用している場合は、ご自身のゾーンで Cloudflare がすでに何をブロックしているかを確認し、2 つの層を整合させてください。

pay per crawl では、価格が設定されたページをリクエストしたクローラーは「crawler-price ヘッダー付きの 402 Payment Required 応答」を受け取り、支払い意思を示して再試行できます。クローラーは Web Bot Auth の提案に沿ってリクエストに署名するため、他者になりすまして支払うことはできません。Cloudflare は、pay per crawl は「現在プライベートベータ中」だとしています。

Web Bot Auth はまだ標準ではありません。2026 年 10 月 20 日時点で、IETF の webbotauth ワーキンググループには、2026 年 9 月 1 日付の draft-ietf-webbotauth-httpsig-protocol-00 というワーキンググループ文書が 1 件あるだけで、RFC はありません。標準化過程の仕様に関する憲章上の 2026 年 4 月のマイルストーンは、すでに過ぎています。個人ドラフトの draft-illyes-webbotauth-jafar は、クローラーの範囲ファイルに共通の JSON 形式を提案しています。それらが確定するまでは、確認できるのは IP の範囲と逆引き DNS です。

ポリシーが何を変えたかを測定する

ポリシーの効果は、すべてのボットのリクエストで検証結果を記録し、前後を比較して測定します。

  • 検証済みボットごとの割合。 ボットを名乗った各リクエストの横に、crawler.name と crawler.verified を記録します。失敗した主張の割合が、偽装率です。名前ごとの割合からは、実際にサイトをクロールしている運営者が分かります。
  • AI からの参照。 chatgpt.com、perplexity.ai、claude.ai、copilot.microsoft.com、gemini.google.com から参照された訪問数を数えます。アシスタントからのトラフィックにはリファラーが付かないものもあるため、その数は下限として扱ってください。
  • クロール量。 変更の前後で、各ボットからの 1 日あたりのリクエスト数を比較します。Search Console のクロール統計レポートが対象とするのは Google のクローラーだけなので、他のボットは自前のログから取得する必要があります。

AI 検索ボットをブロックした後に引用が減ったなら、そのボットについての答えが得られたことになります。

AI クローラーのポリシーを見直す時期

運営者がボットを追加したとき、範囲ファイルを変更したとき、トークンを廃止したとき、そして少なくとも四半期に 1 回は、AI クローラーのポリシーを見直してください。OpenAI のボットのページには現在 OAI-AdsBot が載っており、専用の adsbot.json があります。当社のクローラーフィードにはそのファイルが含まれておらず、上のコードにもその名前はないため、OAI-AdsBot の主張は、お客様が方針を決めるまで第 3 層に回ります。

この構成にできないことは、はっきりさせておきましょう。

  • 証明できるのは、範囲を公開しているボットだけです。 Bytespider と Meta-ExternalAgent は検証できず、どちらの偽装も本物と全く同じに見えます。
  • 新しいプレフィックスには遅れがあります。 今日追加されたプレフィックスの検証には最大で約 2 日かかることがあり、本物のボットが一時的に第 2 層で失敗することがあります。
  • IP のブロックは逆効果になることがあります。 Anthropic は、同社の IP をブロックすると「オプトアウトが正しく、または継続的に保証されない場合がある。当社が robots.txt を読む能力を妨げるため」と警告しています。第 2 層で実施している場合でも、robots.txt の Disallow は残してください。
  • ルックアップは User-Agent を見られません。 偽装の確認は、お客様のコード内の名前の比較で行います。
  • 第 2 層は、名前を名乗らないスクレイパーを見逃します。 それは第 3 層の仕事です。

まず自分のログから始めてください。今週 GPTBot や ClaudeBot を名乗ったアドレスを 1 つ選び、ルックアップにかけて、crawler.name を名乗った名前と比較してみてください。

よくある質問

AI クローラーはブロックすべきですか?

ボットの役割によります。コンテンツがモデルの学習に使われることに異論があるなら、GPTBot や ClaudeBot などの学習用クローラーをブロックします。引用されたいなら、OAI-SearchBot などの AI 検索ボットは許可します。ユーザーの操作で起動するフェッチャーには、robots.txt に頼らずレート制限をかけます。

robots.txt は AI クローラーを止められますか?

従うことを選んだものだけです。RFC 9309 は、そのルールは「アクセス認可の一形態ではない」と述べています。GPTBot の User-Agent を使うスクレイパーは無視するため、実施にはリクエストが届く場所で IP を確認する必要があります。

AI クローラーをブロックすると SEO に悪影響がありますか?

Google-Extended トークンをブロックしても Google 検索には影響しません。Google は、サイトの検索への掲載に「影響しない」としています。AI での利用を止めるために Googlebot をブロックすると、検索から外れます。OAI-SearchBot などの AI 検索ボットをブロックすると、そのアシスタントの検索回答には表示されなくなります。

Google-Extended を IP でブロックできますか?

できません。Google-Extended は robots.txt のトークンであり、独自の User-Agent はありません。実際の取得は Google の既存のクローラーが行うため、制御手段はそのトークンに対する robots.txt のルールだけです。Applebot-Extended も同様で、Apple はウェブページをクロールしないとしています。

GPTBot を名乗るリクエストが本物かどうか、どうすれば分かりますか?

接続元の IP が OpenAI の公開している gptbot.json に含まれているか確認します。逆引き DNS は役に立ちません。確認した GPTBot と ClaudeBot のアドレスには PTR レコードがありませんでした。ルックアップは threat.crawler.verified とボット名を返すので、コード側でその名前を User-Agent と比較します。

すべての AI クローラーを IP で検証できますか?

できません。Bytespider について ByteDance の範囲ファイルは見つからず、Meta も Meta-ExternalAgent の範囲を公開していないため、どちらも IP では検証できません。User-Agent を信用せず、レート制限をかけてください。

執筆者

GeoIPHub チーム

GeoIPHub API を支えるチーム

GeoIPHub IP インテリジェンス API とそのスキャナーを開発している GeoIPHub チームが執筆しています。下書きは AI の支援を受けて作成し、チームが編集しています。API の例は、「説明用」と明記したものを除き、本番 API に対する実際のルックアップです。

この記事をシェア
その他の記事

関連 記事