robots.txt は、サイトのルートに置くテキストファイルで、クローラーに「どの URL を取得してよいか」を伝えます。1994 年に始まった慣習ですが、2022 年 9 月に IETF の標準 RFC 9309 になりました。書式は単純でも、つまずきやすい規則が 3 つあります。クローラーが従うのは 1 つのグループだけ、行の順番ではなく長いルールが勝つ、そしてサーバーエラーは「全部禁止」とみなされる、の 3 つです。
この記事では、それぞれの規則を実際のファイルで確かめます。判定は 2026-10-02 に 2 つのライブラリで行いました。Scrapy が標準で使う Protego 0.7.0 と、Python 3.12 の urllib.robotparser です。両者の結果が食い違う箇所では、RFC と Google の仕様に沿っているのは Protego のほうです。なお CPython は Python 3.13.14 と 3.14.5 でこのモジュールを RFC 9309 に沿って書き直したため(gh-138907)、2026-10-03 に Python 3.14.7 でも同じファイルを判定し、結果が変わる箇所を併記しました。
置き場所と、日本で誰が読むか
クローラーが取りに来るのは、ホストの直下にある小文字の /robots.txt だけです(RFC 9309 2.3 節)。効力はスキーム・ホスト・ポートが同じ URL に限られます。Google の解説(Google による robots.txt の仕様の解釈)にあるとおり、https://example.jp/robots.txt は http://example.jp/ にも https://blog.example.jp/ にも効きません。サブディレクトリに置いた /blog/robots.txt は読まれないので、レンタルサーバーで WordPress を /blog/ に入れている場合も、robots.txt はドメイン直下に置く必要があります。
日本のウェブ検索で見れば、読む相手はまず Googlebot です。Yahoo! JAPAN は 2010 年 7 月に検索エンジンとして Google を採用すると発表しており(ヤフー株式会社のプレスリリース)、この構成である限り、Yahoo! 検索のウェブ結果に出るかどうかは Google のクロールで決まります。そのうえで Bing の Bingbot、Apple の Applebot、各社の AI クローラーが同じファイルを読みます。
ファイルは UTF-8 のプレーンテキストにします。Google は UTF-8 以外の文字を無視することがあると書いています。日本語のパスを含むルールは、比較の前に UTF-8 でパーセントエンコードされます。そのため Disallow: /ブログ/下書き/ と書いても、/%E3%83%96%E3%83%AD%E3%82%B0/%E4%B8%8B%E6%9B%B8%E3%81%8D/a へのリクエストは正しく止まります。Shift_JIS で保存すると、このバイト列が一致しなくなります。
もう 1 つの前提として、robots.txt は「お願い」であってアクセス制限ではありません。RFC 9309 の 3 節は、パスを書けばむしろ公開されると注意しています。管理画面の URL を隠す目的には使えず、守りたいページには Basic 認証などが必要です。また、クロールを止めてもインデックスからは消えません。Google は、ブロックした URL でも他ページからリンクされていればスニペットなしで表示することがあると説明しています。検索結果から外すには、ページをクロールさせたうえで noindex を返します。robots.txt で同じページを止めると、クローラーは noindex を読めなくなります。
クローラーが従うのは 1 つのグループだけ
グループとは、1 行以上の User-agent とそれに続くルールのまとまりです。クローラーは自分の名前(プロダクトトークン)に一致するグループを探し、見つかればそのグループだけに従います。* のグループを使うのは、名前で一致するグループが 1 つもないときだけです(RFC 9309 2.2.1 節)。Google も、* と名前付きのグループは合成しないと明記しています。
会員ページ /mypage/ を全クローラーに禁止し、「Bing には検索結果ページも止めたい」と Bingbot 用のブロックを足した例です。ZeroTool のジェネレーターでそのまま作れます。
User-agent: *
Disallow: /mypage/
User-agent: Bingbot
Disallow: /search/
両パーサーとも、Bingbot は /mypage/ を取得してよい、Googlebot は /mypage/ を取得できない、と判定しました。Bingbot は自分のグループしか読まないので、* の Disallow: /mypage/ は効かなくなります。Bing のヘルプも、Bingbot は自分用のセクションを見つけると汎用セクションを無視するので、共通のルールを繰り返し書くよう求めています(How to Create a robots.txt File)。Bingbot のブロックにも Disallow: /mypage/ を書けば直ります。
同じ名前のグループが複数あれば 1 つに合成されます。また Google は、User-agent / Allow / Disallow 以外の行(Sitemap など)はグループを区切らないと説明しています。空行にも区切りの意味はありません。
Allow と Disallow:行の順番ではなく長さで決まる
選ばれたグループの中では、URL のパスの先頭から一致するルールのうち、一致したバイト数が最も多いものが使われます。ファイル内の順番は関係ありません。長さが同じ Allow と Disallow がぶつかったときは、RFC では Allow を使うべき(SHOULD)、Google は「制限の少ないほう」を使う、としています(RFC 9309 2.2.2 節)。どのルールにも一致しなければ取得してよい、という扱いです。
特殊文字は 2 つで、* は任意の文字列、$ は URL の末尾を表します。ルールはもともと前方一致なので、末尾の * は意味を持ちません。
具体例として WordPress を見ます。WordPress は、ドキュメントルートに robots.txt ファイルがないとき、PHP で仮想の robots.txt を返します。do_robots() が Disallow: /wp-admin/ と Allow: /wp-admin/admin-ajax.php を書き、サイトが検索エンジンに公開されていればサイトマップの行が加わります。これはジェネレーターで次のように再現できます。
User-agent: *
Disallow: /wp-admin/
Allow: /wp-admin/admin-ajax.php
Sitemap: https://example.jp/wp-sitemap.xml
ここに、サイト内検索の結果ページ(WordPress では ?s= パラメーター)を止める Disallow: /*?s= を足したファイルで判定すると、次のようになりました。
| パス | 決め手になるルール(RFC 9309) | Protego | urllib.robotparser(Python 3.12) | urllib.robotparser(Python 3.14.7) |
|---|---|---|---|---|
/wp-admin/admin-ajax.php | Allow: /wp-admin/admin-ajax.php(24 バイト)が Disallow: /wp-admin/(10)より長い | 許可 | 拒否 | 許可 |
/wp-admin/options.php | Disallow: /wp-admin/ | 拒否 | 拒否 | 拒否 |
/?s=ラーメン(エンコード済み) | Disallow: /*?s= | 拒否 | 許可 | 拒否 |
/blog/?s=test | Disallow: /*?s= | 拒否 | 許可 | 拒否 |
/2026/10/hello/ | 一致なし | 許可 | 許可 | 許可 |
Python 3.12 の urllib.robotparser は RFC 以前の流儀で、ファイルの上から最初に一致した行を使い、* と $ を解釈しません。そのため WordPress 標準の robots.txt でさえ admin-ajax.php を拒否と判定し、Googlebot や Bingbot の判定とは一致しません。Python 3.13.14・3.14.5 以降は最長一致と *・$ に対応し、この表では 3.14.7 の結果が Protego と同じになりました。自作のスクリプトでこのモジュールを使っているなら、まず実行環境の Python のバージョンを確認してください。3.12 以前なら、Allow を対応する Disallow より上に書き、ワイルドカードを避けてください。
もう 1 つ注意したいのは、末尾のスラッシュがないルールも前方一致だという点です。Disallow: /member は /members も /member-list.html も止めます。また、パスの大文字・小文字は区別されるので、Disallow: /Admin/ では /admin/ は止まりません。
取得できないときの扱いと、レンタルサーバーで起きること
ルールの中身と同じくらい大事なのが、ファイルを取得できなかったときの挙動です。
/robots.txt の応答 | RFC 9309 | |
|---|---|---|
| 2xx | 解釈できるルールに従う | 同じ。不正な行は無視。HTML が返ってきても、ルールを抜き出そうとする |
| 3xx | 5 回以上リダイレクトをたどる | 5 回たどってだめなら 404 扱い |
| 4xx | 「利用不可」。何でも取得してよい(MAY) | 429 以外の 4xx は「制限なし」 |
| 5xx・タイムアウト | 「到達不能」。全部禁止とみなす | 最初の 12 時間はクロール停止。その後 30 日は最後に取得できた版を使い、それ以降はサイトが正常なら robots.txt がないものとして扱う |
| サイズ | 少なくとも 500 KiB は解析する | 500 KiB を超えた部分は無視 |
| キャッシュ | 24 時間を超えて古い版を使わない(到達不能時を除く) | 通常は最長 24 時間。Cache-Control: max-age で増減することがある |
共用レンタルサーバーで問題になりやすいのは 4xx と 5xx の行です。WAF や国外 IP 制限がクローラーの robots.txt 取得に 403 を返すと、Google は「制限なし」と解釈して全部クロールします。逆に、メンテナンス中に /robots.txt まで 503 を返すと、Google はそのホストのクロールを止めます。ブラウザでページが見えていても起こるので、メンテナンス画面の設定では /robots.txt を除外しておくと安全です。
AI クローラー:robots.txt とエックスサーバーの「AIクローラー遮断設定」
AI 各社は、学習用・自社検索用・ユーザーの依頼による取得用に、別々のトークンを用意するようになりました。
| トークン | 運営 | 拒否したときの意味(各社の公式文書) |
|---|---|---|
GPTBot | OpenAI | 学習に使わないでほしいという意思表示(OpenAI のクローラー一覧) |
OAI-SearchBot | OpenAI | ChatGPT 検索の回答に表示されない(ナビゲーション用のリンクは除く) |
ClaudeBot / Claude-SearchBot / Claude-User | Anthropic | 学習・検索用インデックス・ユーザーの依頼による取得。3 つとも robots.txt に従う(Anthropic ヘルプ) |
Google-Extended | Gemini の学習とグラウンディング。Google 検索への掲載や順位には影響しない(Google の一般的なクローラー) | |
Applebot-Extended | Apple | Apple の基盤モデルの学習。それ自体はクロールしない(About Applebot) |
PerplexityBot | Perplexity | Perplexity の検索結果(Perplexity Crawlers) |
CCBot | Common Crawl | 公開クロールデータ。学習データの供給源として使われることが多い(CCBot) |
Google-Extended と Applebot-Extended は制御用のトークンで、アクセスログには現れません。普段の Googlebot や Applebot が取得したデータの使い道を、このトークンへのルールで決めます。
エックスサーバーには、サーバーパネルの「AIクローラー遮断設定」があります。マニュアルによれば、これは robots.txt ではなく、対象のクローラーからのアクセスそのものを遮断する機能で、初期状態は OFF、ドメインごとに設定します。遮断対象には GPTBot や ClaudeBot だけでなく、OAI-SearchBot、Claude-SearchBot、PerplexityBot、ChatGPT-User、Claude-User も含まれます。ON にすると学習を止められる一方で、ChatGPT や Claude、Perplexity の検索・引用からも外れることになります。「学習には使わせないが AI 検索には出たい」なら、robots.txt で GPTBot、ClaudeBot、Google-Extended、CCBot だけを拒否するほうが目的に合います。ただし robots.txt は依頼にすぎないので、従わないクローラーはサーバー側で止めるしかありません。
公開前後の確認方法
Search Console の robots.txt レポート(設定 → robots.txt)には、プロパティの上位 20 ホストについて、Google が取得した robots.txt、取得日時、解析できなかった行が表示されます。修正後に再クロールをリクエストすることもできます。個別の URL を判定する機能はないので、1 ページ単位では URL 検査ツールを使います。以前の「robots.txt テスター」は 2023 年に提供終了しました。Bing Webmaster Tools には今も robots.txt テスターがあり、Bingbot として URL を判定して、止めている行を表示します。
手元で確かめるなら、ファイルを取得して RFC に沿ったパーサーにかけます。
from protego import Protego
import urllib.request
body = urllib.request.urlopen("https://example.jp/robots.txt").read().decode("utf-8")
rp = Protego.parse(body)
for path in ["/wp-admin/admin-ajax.php", "/?s=test", "/mypage/"]:
print(path, rp.can_fetch("https://example.jp" + path, "Googlebot"))
curl -sI https://example.jp/robots.txt でステータスが 200、Content-Type が text/plain であることも見ておきます。WordPress の仮想 robots.txt は text/plain; charset=utf-8 を返します。
ZeroTool のジェネレーターで書くときの注意
robots.txt ジェネレーターでは、ブロックごとに *・Googlebot・Bingbot を選ぶか、「カスタム」で任意のトークンを入力し、許可・拒否の行とサイトマップ URL を足していきます。この記事のファイル例はすべてこのツールで作り、テストでツールのコードと照合しています。
- 開いた直後は
User-agent: *とDisallow: /、つまりサイト全体の拒否になっています。ステージング環境には合っていますが、本番に置く前に必ず書き換えてください。 - ルールのないブロックは空の
Disallow:として出力され、そのクローラーにはすべて許可になります。 - 1 ブロックに入れられる User-agent は 1 つです。パスは入力どおりに出力され、
/で始まっているか、ワイルドカードが意図どおりかは検査しません。 - サイトマップは 1 行だけ末尾に書きます。
Crawl-delayは出力しません(Google は読みませんが、Bing は 1〜20 秒の範囲で従います)。
WordPress の場合、ドキュメントルートに robots.txt を置くと、サーバーがそのファイルを返すので仮想 robots.txt は使われなくなります。生成したファイルに Allow: /wp-admin/admin-ajax.php とサイトマップの行を残しておくと、元の動作を保てます。検索結果から外したいページには、robots.txt ではなく meta タグ生成ツールで作る noindex を使ってください。