- SEO
robots.txt・sitemap.xmlの書き方|テンプレートと検証手順
robots.txt はクローラーに「どこを巡回してよいか」を伝えるファイルで、sitemap.xml は「巡回してほしいURLの一覧」を伝えるファイルです。 前者は巡回の範囲を絞り、後者は巡回の入口を増やします。2つは役割が逆向きなので、セットで設計すると迷いが減ります。
この記事では、Google の公式仕様(RFC 9309 準拠)を確認したうえで、構文の早見表、そのまま使えるテンプレート(一般サイト/WordPress/静的サイト/AIクローラーだけ拒否)、Python で「どのURLがどのクローラーに許可されるか」を判定した実行結果、sitemap.xml の自動生成スクリプトと xmllint での検証結果をまとめました。Python 標準の urllib.robotparser と Google の判定ルールが実際に食い違うケースも、出力つきで紹介します。
robots.txt・sitemap.xmlの早見表
| やりたいこと | 書き方 | ポイント |
|---|---|---|
| 全クローラーに全ページを許可 | User-agent: * + Disallow:(空) |
空の Disallow は「制限なし」 |
| 全ページの巡回を止める | User-agent: * + Disallow: / |
公開前の検証環境などに |
| ディレクトリを除外 | Disallow: /admin/ |
前方一致。/admin/ 以下すべて |
| 除外の中の一部だけ許可 | Disallow: /admin/ + Allow: /admin/public/ |
Google は長いパスのルールが優先 |
| パラメータ付きURLを除外 | Disallow: /*?sort= |
* は0文字以上の任意文字列 |
| 拡張子で除外 | Disallow: /*.pdf$ |
$ はURLの末尾 |
| AI学習用クローラーだけ拒否 | User-agent: GPTBot 等 + Disallow: / |
検索用クローラーは別名なので影響なし |
| サイトマップの場所を伝える | Sitemap: https://example.com/sitemap.xml |
絶対URL。グループの外に書ける |
| 検索結果から消す | robots.txt では不可 | noindex(meta / X-Robots-Tag)を使う |
| sitemap.xml の上限 | 1ファイル 50,000 URL / 50MB(非圧縮) | 超えたら分割して sitemap index |
| priority / changefreq | 書いてもよいが Google は無視 | lastmod を正確に書くほうが効果的 |
| 置き場所 | https://ホスト/robots.txt(ルート直下) |
ホスト・プロトコル・ポートごとに別扱い |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「robots.txt・sitemap.xmlの書き方|テンプレートと検証手順」
robots.txtの基本:Googleの仕様を確認
robots.txt の規格は RFC 9309(Robots Exclusion Protocol)で、Google はこれに沿って処理します。 公式ドキュメント(Google 検索セントラル「Google による robots.txt の指定の解釈」)で確認した主な仕様は次のとおりです。
| 項目 | Google の仕様 |
|---|---|
| 準拠規格 | RFC 9309 |
| サイズ上限 | 500KiB。超えた部分は無視される |
| 文字コード | UTF-8 のプレーンテキスト。改行は CR、CR/LF、LF のいずれか |
| 有効なフィールド | user-agent、allow、disallow、sitemap の4つ(crawl-delay は Google 非対応) |
| 大文字・小文字 | フィールド名は区別しない。パスは区別する(/Admin/ と /admin/ は別) |
| 適用範囲 | 置いたホスト・プロトコル・ポートのみ(www ありなし、サブドメインは別ファイル) |
| キャッシュ | 通常最大24時間キャッシュされる |
| 3xx | 少なくとも5回までリダイレクトを追い、それ以上は 404 扱い |
| 4xx(429除く) | robots.txt がないものとして扱う(=制限なし) |
| 5xx | 最初の12時間は巡回停止、その後30日間は最後に取得できた内容を使う |
構文の早見表
| 記号・フィールド | 意味 | 例 | マッチする例 |
|---|---|---|---|
User-agent: |
ルールを適用するクローラー名 | User-agent: Googlebot |
Googlebot |
User-agent: * |
名前で指定されていない全クローラー | User-agent: * |
個別指定のないクローラー |
Disallow: |
巡回しないパス(前方一致) | Disallow: /tmp/ |
/tmp/a.html |
Allow: |
巡回してよいパス | Allow: /tmp/ok.html |
/tmp/ok.html |
* |
0文字以上の任意文字列 | Disallow: /*.json |
/api/a.json、/a.json?x=1 |
$ |
URLの末尾 | Disallow: /*.json$ |
/a.json(/a.json?x=1 は対象外) |
# |
コメント | # 管理画面 |
― |
Sitemap: |
サイトマップの絶対URL | Sitemap: https://example.com/sitemap.xml |
― |
最長一致のルール(AllowとDisallowが両方当たるとき)
Google は、URLに当たるルールのうち「パスが最も長い(具体的な)ルール」を採用し、長さが同じなら制限の少ないほう(Allow)を採用します。 書いた順番は関係ありません。
| ルール | URL | 当たるルール | 結果 |
|---|---|---|---|
Disallow: /admin/ Allow: /admin/public/ |
/admin/public/help.html |
Allow(14文字)が Disallow(7文字)より長い | 許可 |
Disallow: /admin/ Allow: /admin/public/ |
/admin/login |
Disallow のみ | 拒否 |
Allow: /page Disallow: /page |
/page |
同じ長さ | 許可(制限が少ないほう) |
Allow: /$ Disallow: / |
/ |
/$ のほうが長い |
許可(トップだけ許可) |
Allow: /$ Disallow: / |
/news |
Disallow のみ | 拒否 |
User-agentのグループの書き方
1つのクローラーに適用されるグループは1つだけです。 Google は「最も具体的に名前が一致するグループ」を使い、それ以外のグループ(* を含む)は読みません。
User-agent: *
Disallow: /admin/
User-agent: Googlebot
Disallow: /tmp/
この場合、Googlebot には Disallow: /tmp/ だけが適用され、/admin/ は巡回対象になります。Googlebot にも /admin/ を除外したいなら、Googlebot のグループにも Disallow: /admin/ を書きます。名前で個別グループを作ったら、共通ルールもそこにコピーするのが基本です。
複数のクローラーに同じルールを当てたいときは、User-agent 行を続けて書きます。
User-agent: GPTBot
User-agent: ClaudeBot
Disallow: /
コピペ用robots.txtテンプレート
一般的なサイト(管理画面と検索結果ページを除外)
User-agent: *
Disallow: /admin/
Disallow: /search
Disallow: /*?sort=
Disallow: /*?sessionid=
Sitemap: https://www.example.com/sitemap.xml
並び替えやセッションIDのパラメータは、同じ内容のURLが大量にできやすいため、巡回から外すとクロールの無駄が減ります。CSS・JavaScript・画像は、ページの表示確認に使われるので除外しないようにします。
WordPress
WordPress(5.5以降)は、物理ファイルがない場合に仮想の robots.txt を返し、コアのサイトマップ wp-sitemap.xml も自動生成します。仮想 robots.txt の既定の中身は次の形です。
User-agent: *
Disallow: /wp-admin/
Allow: /wp-admin/admin-ajax.php
Sitemap: https://www.example.com/wp-sitemap.xml
カスタマイズしたいときは、ドキュメントルートに robots.txt を置くと物理ファイルが優先されます。追加の例です。
User-agent: *
Disallow: /wp-admin/
Allow: /wp-admin/admin-ajax.php
Disallow: /?s=
Disallow: /search/
Sitemap: https://www.example.com/wp-sitemap.xml
| 項目 | 理由 |
|---|---|
Allow: /wp-admin/admin-ajax.php |
テーマやプラグインの表示に使われることがあるため |
Disallow: /?s= |
サイト内検索の結果ページ。無数にURLができる |
/wp-content/ は除外しない |
CSS・JS・画像が入っており、表示確認に必要 |
| Sitemap の URL | SEOプラグインを使う場合はそのプラグインのサイトマップURLに合わせる |
静的サイト(Hugo・Astro・Next.jsの静的出力など)
User-agent: *
Disallow:
Sitemap: https://www.example.com/sitemap-index.xml
静的サイトは除外したいページがほとんどないため、「全許可+Sitemap」だけで十分なことが多いです。ビルドツールのプラグインでサイトマップを生成している場合は、出力されるファイル名(sitemap.xml か sitemap-index.xml か)を確認して合わせます。
公開前の検証環境(ステージング)
User-agent: *
Disallow: /
検証環境は robots.txt に加えて Basic 認証などのアクセス制限をかけるのが確実です。robots.txt はアクセス制御ではなく「お願い」なので、URLが外部から知られると検索結果にURLだけ表示されることがあります。本番公開時にこのファイルを持ち込まないよう、環境ごとにファイルを分けておくと安心です。
AIクローラーをブロックする書き方
主なAIクローラーの名前と用途(各社公式で確認)
AIクローラーは「モデルの学習用」と「AI検索・回答用」で名前が分かれています。 学習だけ拒否して、AI検索には表示させる、という使い分けができます。
| 運営 | User-agent トークン | 用途(公式説明の要約) | robots.txt |
|---|---|---|---|
| OpenAI | GPTBot |
生成AIの基盤モデルの学習に使う可能性があるコンテンツの収集 | 従う |
| OpenAI | OAI-SearchBot |
ChatGPT の検索機能にサイトを表示するため | 従う |
| OpenAI | ChatGPT-User |
ユーザーの操作をきっかけにしたアクセス(自動巡回ではない) | 適用されない場合があると公式に記載 |
| Anthropic | ClaudeBot |
モデルの学習・開発用のWebコンテンツ収集 | 従う |
| Anthropic | Claude-SearchBot |
検索結果の品質向上のためのインデックス | 従う |
| Anthropic | Claude-User |
ユーザーが Claude に質問したときのWeb取得 | 従う |
Google-Extended |
Gemini の学習と、Gemini アプリ等でのグラウンディングへの利用可否 | 従う | |
| Common Crawl | CCBot |
公開Webのアーカイブ(多くのAI学習データの元になる) | 従う |
Google-Extended は「巡回するクローラー」ではなく、利用可否を伝えるためのトークンです。 実際の巡回は Googlebot が行うため、HTTP の User-Agent 文字列に Google-Extended は現れません。Google は公式に「Google-Extended は Google 検索への掲載に影響せず、ランキングの要素にも使わない」と説明しています。
テンプレート:検索は許可、AI学習は拒否
# AI学習用クローラーは拒否
User-agent: GPTBot
User-agent: ClaudeBot
User-agent: Google-Extended
User-agent: CCBot
Disallow: /
# 検索エンジン・AI検索を含むその他のクローラー
User-agent: *
Disallow: /admin/
Allow: /admin/public/
Disallow: /*?sort=
Disallow: /*.pdf$
Sitemap: https://www.example.com/sitemap.xml
どこまでブロックするかの判断表
| 方針 | 書くトークン | 結果 |
|---|---|---|
| AI学習には使わせない。AI検索には表示したい | GPTBot ClaudeBot Google-Extended CCBot |
学習は拒否、ChatGPT検索・Claude検索・Google検索には表示 |
| AI検索にも表示しない | 上記+OAI-SearchBot Claude-SearchBot |
AIの検索機能からの参照が減る |
| すべて許可(AIからの流入も歓迎) | 何も書かない | 既定どおり巡回される |
| Google 検索にも出さない | Googlebot を拒否 |
通常は選ばない(検索流入がなくなる) |
AI検索(AI Overviews や ChatGPT 検索など)からの流入を重視するなら、検索用のトークンは拒否しないのがおすすめです。新しいクローラーは今後も増えるので、半年に一度ほど各社の公式ページで名称を確認すると安心です。
Pythonでrobots.txtの許可・拒否を判定する(実行結果つき)
公開前に「どのURLが、どのクローラーに許可されるか」を一覧で確認すると、書き間違いを防げます。 ここでは Python 標準の urllib.robotparser と、Google の仕様(最長一致・ワイルドカード)に沿って実装されたライブラリ Protego(Scrapy が使っているパーサー)を並べて判定しました。
- 環境:Python 3.11.15、Protego 0.7.0(
pip install protego) - 判定対象:上の「検索は許可、AI学習は拒否」テンプレートを
robots.txtとして保存
# check_robots.py
import sys
from urllib.robotparser import RobotFileParser
from protego import Protego
text = open("robots.txt", encoding="utf-8").read()
rp = RobotFileParser()
rp.parse(text.splitlines())
pg = Protego.parse(text)
agents = ["Googlebot", "Bingbot", "GPTBot", "ClaudeBot", "Google-Extended", "CCBot", "OAI-SearchBot"]
urls = [
"https://www.example.com/",
"https://www.example.com/admin/login",
"https://www.example.com/admin/public/help.html",
"https://www.example.com/items?sort=price",
"https://www.example.com/docs/guide.pdf",
"https://www.example.com/docs/guide.pdf?v=2",
]
print(f"Python {sys.version.split()[0]}")
print(f"{'User-agent':<16} {'URL':<34} robotparser Protego")
for ua in agents:
for u in urls:
path = u.replace("https://www.example.com", "")
a = "許可" if rp.can_fetch(ua, u) else "拒否"
b = "許可" if pg.can_fetch(u, ua) else "拒否"
mark = " <- 差" if a != b else ""
print(f"{ua:<16} {path:<34} {a:<10} {b}{mark}")
print("site_maps():", rp.site_maps())
実行結果(抜粋。GPTBot・ClaudeBot・Google-Extended・CCBot は全URLで「拒否」、Bingbot・OAI-SearchBot は Googlebot と同じ結果でした):
Python 3.11.15
User-agent URL robotparser Protego
Googlebot / 許可 許可
Googlebot /admin/login 拒否 拒否
Googlebot /admin/public/help.html 拒否 許可 <- 差
Googlebot /items?sort=price 許可 拒否 <- 差
Googlebot /docs/guide.pdf 許可 拒否 <- 差
Googlebot /docs/guide.pdf?v=2 許可 許可
GPTBot / 拒否 拒否
GPTBot /admin/public/help.html 拒否 拒否
ClaudeBot / 拒否 拒否
Google-Extended / 拒否 拒否
CCBot / 拒否 拒否
OAI-SearchBot / 許可 許可
OAI-SearchBot /admin/public/help.html 拒否 許可 <- 差
site_maps(): ['https://www.example.com/sitemap.xml']
標準のrobotparserとGoogleの判定が違う3つのポイント
AIクローラーの拒否はどちらも同じ結果ですが、Allow やワイルドカードを含む行では判定が分かれました。 原因は次の3つです。
| 違い | urllib.robotparser(3.11) | Google(RFC 9309) / Protego |
|---|---|---|
| Allow と Disallow の優先 | 上から順に最初に当たったルール | 最も長いパスのルール |
*(パス中のワイルドカード) |
解釈しない(文字としての * 扱い) |
0文字以上の任意文字列 |
$(末尾一致) |
解釈しない | URLの末尾 |
順番の違いは、次のテストで確認できます。
# order_test.py
from urllib.robotparser import RobotFileParser
from protego import Protego
a = """User-agent: *
Disallow: /admin/
Allow: /admin/public/
"""
b = """User-agent: *
Allow: /admin/public/
Disallow: /admin/
"""
tie = """User-agent: *
Allow: /page
Disallow: /page
"""
url = "https://www.example.com/admin/public/help.html"
for name, t in [("Disallowが先", a), ("Allowが先", b)]:
rp = RobotFileParser(); rp.parse(t.splitlines())
print(f"{name}: robotparser={rp.can_fetch('Googlebot', url)} Protego={Protego.parse(t).can_fetch(url, 'Googlebot')}")
rp = RobotFileParser(); rp.parse(tie.splitlines())
u="https://www.example.com/page"
print(f"同じ長さで競合: robotparser={rp.can_fetch('Googlebot', u)} Protego={Protego.parse(tie).can_fetch(u, 'Googlebot')}")
Disallowが先: robotparser=False Protego=True
Allowが先: robotparser=True Protego=True
同じ長さで競合: robotparser=True Protego=True
Google 向けの確認には、最長一致とワイルドカードに対応したパーサー(Protego や Google 公開の robots.txt ライブラリ)を使うと、実際の挙動に近い結果になります。標準の robotparser で確認したい場合は、Allow を Disallow より上に書くと両者の結果がそろいやすくなります(Google は順番を見ないので、この並べ方でも問題ありません)。
sitemap.xmlの書き方
基本形(urlset)
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://www.example.com/</loc>
<lastmod>2026-10-01</lastmod>
</url>
<url>
<loc>https://www.example.com/articles/robots-guide</loc>
<lastmod>2026-09-28T10:30:00+09:00</lastmod>
</url>
</urlset>
| タグ | 必須 | 書き方 | Google の扱い |
|---|---|---|---|
urlset |
必須 | 名前空間 http://www.sitemaps.org/schemas/sitemap/0.9 |
― |
url / loc |
必須 | 絶対URL(https:// から)。& は & |
正規URL(canonical)をそろえて書く |
lastmod |
任意 | W3C 日付形式(2026-10-01 または 2026-10-01T10:30:00+09:00) |
正確で一貫していれば使われる |
changefreq |
任意 | daily など |
無視される |
priority |
任意 | 0.0〜1.0 | 無視される |
lastmod は「本文・構造化データ・リンクなど、意味のある更新をした日時」を書きます。 フッターの年表記を変えただけ、のような更新で日付を動かすと、値の信頼性が下がり使われにくくなります。
サイトマップインデックス(sitemapindex)
1ファイルの上限は 50,000 URL または 50MB(非圧縮)です。超える場合は分割し、それらをまとめるインデックスファイルを作ります。
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://www.example.com/sitemap-1.xml</loc>
<lastmod>2026-10-01</lastmod>
</sitemap>
<sitemap>
<loc>https://www.example.com/sitemap-2.xml</loc>
<lastmod>2026-09-28</lastmod>
</sitemap>
</sitemapindex>
| 項目 | 内容 |
|---|---|
| 1サイトマップの上限 | 50,000 URL / 50MB(非圧縮)。gzip 圧縮して配信してもよい |
| 置き場所 | ルート直下が基本(Search Console で送信しない場合、置いたディレクトリ配下にしか効かない) |
| 文字コード | UTF-8 |
| 入れるURL | 検索結果に出したい正規URLだけ(noindex、リダイレクト元、404 は入れない) |
Pythonでsitemap.xmlを自動生成するスクリプト
DBやCMSの記事一覧からサイトマップを作り、50,000件ごとに自動で分割してインデックスまで出力するスクリプトです。標準ライブラリだけで動きます。
# gen_sitemap.py(Python 3.11 で確認)
"""sitemap.xml を自動生成する(50,000件ごとに分割し、sitemap_index.xml を作る)"""
from datetime import date
from pathlib import Path
from xml.sax.saxutils import escape
BASE = "https://www.example.com"
OUT = Path("public")
MAX_URLS = 50_000 # Google の上限:1ファイル 50,000 URL
MAX_BYTES = 50 * 1024 * 1024 # 同じく 50MB(非圧縮)
def load_pages():
"""本来はDBやCMSから取得する。ここではダミーの記事 120,000 件"""
yield ("/", date(2026, 10, 1))
for i in range(1, 120_000):
yield (f"/articles/{i}?lang=ja&ref=top", date(2026, 9, (i % 28) + 1))
def url_entry(path, lastmod):
loc = escape(BASE + path) # & < > をエスケープ(& になる)
return f" <url>\n <loc>{loc}</loc>\n <lastmod>{lastmod.isoformat()}</lastmod>\n </url>\n"
HEAD = '<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'
TAIL = "</urlset>\n"
def write_chunks(pages):
OUT.mkdir(exist_ok=True)
files, buf, size, n = [], [], 0, 0
def flush():
nonlocal buf, size, n
if not buf:
return
name = f"sitemap-{len(files) + 1}.xml"
(OUT / name).write_text(HEAD + "".join(buf) + TAIL, encoding="utf-8")
files.append((name, max_lastmod))
buf, size, n = [], 0, 0
max_lastmod = date.min
for path, lastmod in pages:
entry = url_entry(path, lastmod)
b = len(entry.encode("utf-8"))
if n >= MAX_URLS or size + b + 200 > MAX_BYTES:
flush(); max_lastmod = date.min
buf.append(entry); size += b; n += 1
max_lastmod = max(max_lastmod, lastmod)
flush()
return files
def write_index(files):
lines = ['<?xml version="1.0" encoding="UTF-8"?>',
'<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">']
for name, lastmod in files:
lines += [" <sitemap>", f" <loc>{BASE}/{name}</loc>",
f" <lastmod>{lastmod.isoformat()}</lastmod>", " </sitemap>"]
lines.append("</sitemapindex>")
(OUT / "sitemap_index.xml").write_text("\n".join(lines) + "\n", encoding="utf-8")
if __name__ == "__main__":
files = write_chunks(load_pages())
write_index(files)
for name, _ in files:
p = OUT / name
print(f"{name}: {p.read_text(encoding='utf-8').count('<url>'):,} URL, {p.stat().st_size/1024/1024:.1f} MB")
print("sitemap_index.xml:", len(files), "files")
実行結果(python3 gen_sitemap.py):
sitemap-1.xml: 50,000 URL, 5.9 MB
sitemap-2.xml: 50,000 URL, 6.0 MB
sitemap-3.xml: 20,000 URL, 2.4 MB
sitemap_index.xml: 3 files
生成された sitemap-1.xml の先頭です。& が & にエスケープされています。
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://www.example.com/</loc>
<lastmod>2026-10-01</lastmod>
</url>
<url>
<loc>https://www.example.com/articles/1?lang=ja&ref=top</loc>
インデックス側の lastmod には、そのファイルに含まれるURLの最新の lastmod を入れています。 こうすると、更新があったサイトマップだけを再取得してもらいやすくなります。
xmllintでsitemap.xmlの妥当性をチェックする
サイトマップのエラーで多いのは「& のエスケープ漏れ」と「lastmod の日付形式」です。 どちらも xmllint(libxml2 付属。Ubuntu では apt-get install libxml2-utils)で公開前に見つけられます。
整形式(XMLとして壊れていないか)
xmllint --noout public/sitemap-1.xml public/sitemap_index.xml && echo "OK: well-formed"
OK: well-formed
& をエスケープし忘れたファイルでは、次のエラーになりました(libxml 2.9.14)。
<loc>https://www.example.com/items?color=red&size=m</loc>
bad.xml:4: parser error : EntityRef: expecting ';'
<loc>https://www.example.com/items?color=red&size=m</loc>
^
exit=1
直し方:& を & にする(Python なら xml.sax.saxutils.escape() を通す)。
スキーマ検証(タグと値の形式)
sitemaps.org が配布している公式 XSD を使うと、タグ名や値の形式まで確認できます。ここでは同じ構造の簡易スキーマ(loc 必須、lastmod は日付または日時)を作って試しました。
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" elementFormDefault="qualified">
<xsd:element name="urlset">
<xsd:complexType><xsd:sequence>
<xsd:element name="url" maxOccurs="50000"><xsd:complexType><xsd:sequence>
<xsd:element name="loc" type="xsd:anyURI"/>
<xsd:element name="lastmod" minOccurs="0">
<xsd:simpleType><xsd:union memberTypes="xsd:date xsd:dateTime"/></xsd:simpleType>
</xsd:element>
<xsd:element name="changefreq" type="xsd:string" minOccurs="0"/>
<xsd:element name="priority" type="xsd:decimal" minOccurs="0"/>
</xsd:sequence></xsd:complexType></xsd:element>
</xsd:sequence></xsd:complexType>
</xsd:element>
</xsd:schema>
xmllint --noout --schema sitemap-mini.xsd public/sitemap-3.xml
xmllint --noout --schema sitemap-mini.xsd bad2.xml # lastmod が 2026/10/01
public/sitemap-3.xml validates
bad2.xml:5: element lastmod: Schemas validity error : Element '{http://www.sitemaps.org/schemas/sitemap/0.9}lastmod': '2026/10/01' is not a valid value of the local union type.
bad2.xml fails to validate
lastmod はスラッシュ区切りではなく 2026-10-01 の形で書く、と一目でわかります。CI に xmllint --noout を入れておくと、生成スクリプトを変更したときも安心です。
よくある間違いと直し方
| よくある書き方 | 起きること | こうするとよい |
|---|---|---|
robots.txt に Noindex: を書く |
Google は2019年9月から非対応。効果なし | ページに <meta name="robots" content="noindex"> か X-Robots-Tag ヘッダー |
| noindex したいページを Disallow する | 巡回されないので noindex が読まれず、URLだけ検索結果に残ることがある | noindex が反映されるまでは Disallow しない |
/wp-content/ や CSS・JS を Disallow |
ページの表示を正しく確認できなくなる | 表示に必要なファイルは許可する |
| Googlebot 用グループに共通ルールを書き忘れ | * のグループは読まれず、除外したいページが巡回される |
個別グループにも共通ルールをコピー |
Disallow: /Admin/ と書いた |
パスは大文字・小文字を区別するので /admin/ に効かない |
実際のURLと同じ表記で書く |
| サブドメインにも効くと思っていた | robots.txt はホストごと | blog.example.com/robots.txt を別に置く |
Crawl-delay で Google の頻度を下げる |
Google は非対応 | サーバー負荷は Search Console のクロール統計で確認し、必要ならサーバー側で調整 |
| sitemap に相対URLを書く | 読み込まれない | https:// から始まる絶対URL |
| sitemap に noindex・リダイレクト・404 のURL | 正規URLの判断がぶれる | 検索結果に出したい正規URLだけ入れる |
| priority を細かく調整 | Google は無視する | 正確な lastmod と内部リンクに時間を使う |
| 秘密のページを Disallow で隠す | robots.txt は誰でも読めるので、かえって場所が知られる | 認証・アクセス制限で守る |
Search Consoleでの送信と確認手順
sitemap.xmlを送信する
- Search Console で対象のプロパティを選ぶ
- 左メニューの「サイトマップ」を開く
- 「新しいサイトマップの追加」に
sitemap_index.xmlなど(ドメイン以降の部分)を入力して「送信」 - ステータスが「成功しました」になり、「検出されたページ数」が想定に近いか確認する
インデックスファイルを送信すれば、中の各サイトマップもまとめて読み込まれます。 あわせて robots.txt に Sitemap: 行を書いておくと、Google 以外の検索エンジンにも場所が伝わります。なお、以前あった「ping URL にアクセスして更新を知らせる方法」は Google が2023年に廃止を告知しているため、Search Console への送信と robots.txt への記載を基本にします。
robots.txtを確認する
| 確認したいこと | 方法 |
|---|---|
| Google が取得した robots.txt の内容・取得日時・エラー | Search Console の「設定」→「robots.txt」レポート |
| 特定のURLがブロックされているか | Search Console の「URL検査」 |
| 公開前にローカルで判定 | Google 公開の robots.txt パーサー(オープンソース)や Protego で判定(上のスクリプト) |
| 公開されているか | シークレットウィンドウで https://ホスト/robots.txt を開く |
以前の「robots.txt テスター」は提供が終了しており、現在は上の robots.txt レポートが公式の確認手段です。修正後に早く反映させたいときは、robots.txt レポートから再クロールをリクエストできます。
よくある質問
Q. robots.txt と sitemap.xml は両方必要ですか? A. 小さなサイトなら、どちらもなくても巡回はされます。ただ、robots.txt に Sitemap の場所を書き、sitemap.xml を Search Console に送信しておくと、新しいページが見つかるまでの時間を短くしやすくなります。 除外したいページがなければ robots.txt は「全許可+Sitemap 行」だけで十分です。
Q. GPTBot を拒否すると ChatGPT の検索結果にも出なくなりますか?
A. OpenAI の公式説明では、学習用の GPTBot と ChatGPT 検索用の OAI-SearchBot は別のクローラーです。GPTBot だけを拒否すれば、学習には使われず、検索機能には表示される設定になります。Anthropic の ClaudeBot と Claude-SearchBot も同じ考え方です。
Q. Google-Extended を拒否すると Google 検索の順位に影響しますか? A. Google の公式ドキュメントに「Google 検索への掲載に影響せず、ランキングの要素にも使わない」と明記されています。Gemini の学習やグラウンディングへの利用可否だけを伝えるトークンです。
Q. robots.txt を変更したのに反映されません。 A. Google は robots.txt を通常最大24時間キャッシュします。Search Console の robots.txt レポートで取得日時を確認し、必要なら再クロールをリクエストしてください。
まとめ
- robots.txt は「巡回の範囲」、sitemap.xml は「巡回してほしいURLの一覧」を伝えるファイル
- Google は RFC 9309 準拠。500KiB 上限、パスは大文字小文字を区別、最長一致で判定
- AIクローラーは学習用と検索用で名前が別。GPTBot・ClaudeBot・Google-Extended・CCBot を拒否しても検索には表示される
- Python 標準の robotparser は上から順に判定し、
*と$を解釈しないため、Google 向けの確認は Protego などを使う - sitemap.xml は絶対URL・正確な lastmod・50,000 URL / 50MB で分割。priority と changefreq は Google が無視する
- xmllint で
&のエスケープ漏れや日付形式を公開前に検出できる - 検索結果から外すのは robots.txt ではなく noindex。送信と確認は Search Console で行う
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「robots.txt・sitemap.xmlの書き方|テンプレートと検証手順」
株式会社RJCでは、クローラーとの付き合い方のようなWebの基礎知識を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。