本文へスキップ

llms.txtの設置と検証:Astroでの生成、配信・リンク確認、クロールの観察

llms.txt v2の提案を基に、書式と設置場所、Astroでの自動生成、配信・書式・リンクの検証、Search Consoleでの観察を整理します。検索効果とは分けて扱います。

SHAYOUWORLD 更新 約9分

llms.txtは、サイトの資料を見つけやすくするためのMarkdownの案内ファイルです。導入するときは、誰が何のために読むのかを決め、リンク先を維持できる範囲に絞ります。検索順位やAI引用を保証する仕組みとしては扱いません。

後半では、codeagent.jpがAstroでllms.txtとllms-full.txtを生成している実装、生成物の検証、Search Consoleで見えた取得の記録を載せます。実測値と観察は、いずれも2026年9月14日時点のものです。

v2提案で確認した書式と設置場所

提案元は2026年8月にv2へ更新されています。H1のサイト名を基本に、概要、見出しごとのリンク、説明を書きます。設置場所はルートだけでなくサブパスも想定され、その配下の資料を案内します。資料には読みやすいMarkdown版へのリンクを使う提案も含まれます。

以下は実在するサービスの一覧ではなく、書式を試すための架空例です。

# Example Tools
> 小さなCSV変換ツールの公開ドキュメント。
## 利用ガイド
- [入力形式](https://example.com/docs/input.md): 列名、文字コード、許容する空欄
- [エラー一覧](https://example.com/docs/errors.md): メッセージ別の確認手順
## Optional
- [変更履歴](https://example.com/docs/changelog.md): バージョンごとの変更

実サイトへ設置するときは、例のドメインをそのまま残さず、公開済みの資料へ置き換えます。サブパスに限定するなら /docs/llms.txt のように管理対象に近い場所を選べます。

Astroで配信して確かめる

静的ファイルとして置く

静的ファイルとして扱う場合は public/llms.txt、サブパスなら public/docs/llms.txt に保存します。ビルド後の出力と公開URLの両方を確認してください。確認の手順は次の節にまとめます。

記事コレクションから生成する

記事が増えるサイトでは、手書きのリンク一覧は更新が漏れやすくなります。Astroの静的ファイルエンドポイントは、src/pages/ に .ts を置いて GET をexportする仕組みです。公式ドキュメントのとおり、ファイル名がそのまま出力パスになり(llms.txt.ts → /llms.txt)、静的ビルドではビルド時に呼ばれて本文がファイルとして書き出されます。

codeagent.jpの src/pages/llms.txt.ts の骨格です(固定リンクの部分は省略)。

import { getCollection } from 'astro:content';
import type { APIContext } from 'astro';
import { SITE } from '@/consts';
import { CATEGORIES } from '@/data/categories';
export async function GET(context: APIContext) {
const baseUrl = (context.site ?? new URL(SITE.url)).toString().replace(/\/$/, '');
// 除外ルール: draft: true の記事は載せない。新しい順に並べる
const posts = (await getCollection('posts', ({ data }) => !data.draft)).sort(
(a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf(),
);
const lines: string[] = [];
lines.push(`# ${SITE.name}`); // H1: サイト名
lines.push('');
lines.push(`> ${SITE.description}`); // 引用ブロックの概要
lines.push('');
lines.push('## サイト基本情報');
lines.push('');
lines.push(`- 名称: ${SITE.name}`);
lines.push(`- URL: ${baseUrl}/`);
lines.push(`- Sitemap: ${baseUrl}/sitemap-index.xml`);
lines.push('');
// カテゴリごとに H2 を切り、[title](url): description のリストにする
for (const category of CATEGORIES) {
const inCategory = posts.filter((p) => p.data.category === category.name);
if (inCategory.length === 0) continue;
lines.push(`## ${category.name}`);
lines.push('');
lines.push(category.description);
lines.push('');
for (const post of inCategory) {
lines.push(`- [${post.data.title}](${baseUrl}/posts/${post.id}/): ${post.data.description}`);
}
lines.push('');
}
lines.push('## ツール');
lines.push('');
// ...ツール・固定ページのリンクが続く
return new Response(lines.join('\n'), {
headers: { 'Content-Type': 'text/plain; charset=utf-8', 'Cache-Control': 'public, max-age=3600' },
});
}

設計上の判断は3つです。

  • 除外ルールはfrontmatterに寄せる。 draft: true なら載せません。noindexのタグページなどはそもそも posts コレクションに無いので、ここでは扱いません。
  • URLは context.site から作る。 astro.config.mjs の site を変えれば全リンクが追従します。相対パスにしないのは、読み手がファイルをどこから取得したか分からない場合でも、リンクを解決できるようにするためです。
  • 説明文は description をそのまま使う。 提案の「: の後の説明」に当たります。記事のdescriptionを100〜160字で書く運用にしているので、1行が長くなりすぎません。

llms-full.txtを追加する場合

全文をまとめるllms-full.txtを追加する場合は、対象範囲と更新処理を先に決めます。本文を二重に保守すると訂正漏れが起きるため、同じ原稿から生成し、下書き・非公開情報が混ざらないことをビルド時に検査します。

2026年9月14日に確認した提案にはllms-full.txtの定義がなく、全文を1ファイルに連結する慣行として扱っています。出さなくてもllms.txtは成立します。codeagent.jpの llms-full.txt.ts は、同じコレクションを本文ごと連結します。要点だけ抜きます。

function stripMdxImports(body: string): string {
return body
.replace(/^import\s+.+from\s+['"][^'"]+['"];?\s*$/gm, '')
.replace(/^\s*\n+/, '');
}
// ...posts の取得は llms.txt.ts と同じ
for (const post of posts) {
out.push(`# ${post.data.title}`);
out.push(`URL: ${baseUrl}/posts/${post.id}/`);
out.push(`Published: ${post.data.pubDate.toISOString().slice(0, 10)}`);
if (post.data.updatedDate) out.push(`Updated: ${post.data.updatedDate.toISOString().slice(0, 10)}`);
out.push(`Category: ${post.data.category}`);
out.push(`Tags: ${post.data.tags.join(', ')}`);
out.push('');
out.push(`> ${post.data.description}`);
out.push('');
out.push(stripMdxImports(post.body ?? ''));
out.push('---');
}

post.body はレンダリング前のMDXテキストです。import行だけ落としても、本文中の部品タグは残ります。この問題は後の節で扱います。

生成物を配信・書式・リンクの3段で検証する

生成しているからといって、出力が正しいとは限りません。公開URLから取得したファイルで、配信、書式、リンクの順に確認します。

配信

Terminal window
curl.exe -I https://example.com/llms.txt
curl.exe https://example.com/llms.txt

このコマンドは自分の公開URLに置き換えて実行します。成功時の確認点は、200応答、期待する本文、適切なテキストのContent-Type、ログイン画面やエラーページでないことです。

codeagent.jpで確認したときの応答ヘッダーは次のとおりでした。

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Cache-Control: public, max-age=0, must-revalidate

Content-Type は期待どおりでしたが、Cache-Control はソースに書いた max-age=3600 ではありませんでした。静的ビルドではエンドポイントの戻り値のうち本文だけがファイルになり、ヘッダーはホスティング側(ここではCloudflare Pages)の設定で決まります。変えたい場合は public/_headers に書きます。Response に書いても反映されません。

書式

llmstxt.orgの提案を項目に分け、codeagent.jpの生成物と突き合わせました。

llmstxt.org の提案
codeagent.jp の生成物
H1
プロジェクト名またはサイト名。唯一の必須要素
# codeagent.jp
引用ブロック
任意。短い概要
サイトの description を1行
H2 セクション
リンクの一覧を H2 で区切る
8 セクション(基本情報 / 6 カテゴリ / ツール)
リスト項目
[name](url) が必須、任意で「: 説明」
全 115 項目が [title](url): description
Optional セクション
短い文脈では省略してよい二次情報を置く
未使用(すべて通常セクション)
各ページの .md 版
クリーンな Markdown 版の用意を推奨
未提供(/posts/…/index.html.md も …/.md も 404)
llms-full.txt
定義なし
慣行として提供(106 記事を連結)
設置場所
/llms.txt または任意のサブパス
ルート /llms.txt
2026-09-14 に閲覧した llmstxt.org の記述と、同日に取得した本番ファイルの突き合わせ

必須要素と基本形は満たしていました。満たしていないのはOptionalセクションと各ページの .md 版の2つで、どちらも任意です。.md 版はAstroなら src/pages/posts/[slug].md.ts のような動的エンドポイントで出せますが、全文版の抽出品質を直してから着手する予定です。

リンク

llms.txt内のURLをすべて抜き出して確認しました。

Terminal window
curl -s https://codeagent.jp/llms.txt > llms-live.txt
grep -o '](https://codeagent.jp[^)]*)' llms-live.txt | sed 's/^](//; s/)$//' | sort -u > llms-urls.txt
wc -l llms-urls.txt # 116
fail=0
while read u; do
c=$(curl -s -o /dev/null -w '%{http_code}' "$u")
[ "$c" != "200" ] && { echo "$c $u"; fail=$((fail+1)); }
done < llms-urls.txt
echo "non-200: $fail"
116 llms-urls.txt
non-200: 0

116 URL(記事106件、ツール・固定ページ、全文版へのリンク)はすべて200でした。記事のリンクはコレクションから作るため古くなりにくい一方、ツールや固定ページは手書きの文字列なので、ページを消したときにここが古くなります。

デプロイ後にまとめて回す

3段の確認を一つにまとめると、次の形になります。

#!/usr/bin/env bash
# llms.txt 検証: 配信 / 書式の最低条件 / リンク生存
set -e
BASE="https://codeagent.jp"
curl -sf "$BASE/llms.txt" -o /tmp/llms.txt
head -1 /tmp/llms.txt | grep -q '^# ' || { echo "H1 がない"; exit 1; }
sed -n 3p /tmp/llms.txt | grep -q '^> ' || echo "warn: 引用ブロックがない"
grep -o '](https://[^)]*)' /tmp/llms.txt | sed 's/^](//; s/)$//' | sort -u > /tmp/urls.txt
fail=0
while read u; do
c=$(curl -s -o /dev/null -w '%{http_code}' "$u")
[ "$c" != "200" ] && { echo "$c $u"; fail=$((fail+1)); }
done < /tmp/urls.txt
echo "links: $(wc -l < /tmp/urls.txt), non-200: $fail"
[ "$fail" -eq 0 ]

デプロイ前後の確認全体はAstro × Cloudflare Pagesのリリースゲートで扱っています。

全文版に残るMDXの部品タグ

41,033 B
llms.txt
167 行、H2 8 本、リンク 116
1,661,810 B
llms-full.txt
21,538 行、106 記事(2026-09-11 ビルド)
299 行
全文版に残った部品タグ行
Callout 120 / StatGrid 48 / Compare 44 / Timeline 33 / BarChart 26 ほか
0 / 116
リンク切れ
全 URL が 200
2026-09-14 に本番から取得して計測。行数は wc -l、タグ行は行頭が <大文字 の行を数えた

全文版の中で行頭が < の行は581行、うち大文字で始まる部品タグの行が299行ありました。内訳は <Callout 120、<StatGrid 48、<Compare 44、<Timeline 33、<BarChart 26、<DiagramFrame 14、<PullQuote 11など。加えて <div class="summary-box"> が45箇所です。

StatGrid の stats 配列のようなJSX属性は、Markdownとしては意味のない文字列です。全文版を出していても、部品の中の数値や比較表は構造として伝わっていません。部品を使う編集方針と、全文版の作り方がかみ合っていない状態です。

部品タグを落とす案(本番には未適用)

本番のコードはまだ変えていません。取得した全文版に対して、部品の種類ごとに処理を分ける関数を手元で試しました。

// 自己閉じで使う構造部品は、開始行から行末が "/>" の最初の行までを丸ごと落とす
// 開閉タグ型 (Callout など) はタグ行だけ落として中身の本文は残す
const stripped = body
.replace(/^<(StatGrid|Compare|Timeline|BarChart)\b[\s\S]*?\/>[ \t]*$/gm, '')
.replace(/^<(Callout|PullQuote|DiagramFrame)\b[^\n]*>[ \t]*$/gm, '')
.replace(/^<\/(Callout|PullQuote|DiagramFrame)>[ \t]*$/gm, '')
.replace(/^<div class="summary-box">[ \t]*$/gm, '')
.replace(/^<\/div>[ \t]*$/gm, '')
.replace(/\n{3,}/g, '\n\n');
before: 行頭 < の行 581 / 部品タグ行 299 / 1,661,810 bytes
after : 行頭 < の行 55 / 部品タグ行 10 / 1,531,703 bytes
残り: DiagramFrame 7, svg 8, figure 6, script 3 ほか(インライン SVG と生 HTML)

タグ行は299行から10行に減り、130KBほど軽くなりました。残るのは DiagramFrame(caption属性が複数行)と、記事内に直接書いたSVGやHTMLです。

最初に試した「<大文字 から次の /> まで」という大まかな正規表現は、<Callout> の開始行から遠く離れた別部品の /> までを飲み込み、1.66MBが0.90MBになりました。本文を消していたわけです。部品の種類ごとに閉じ方を分けないと危険だということが、手元の試行で分かりました。

正規表現には限界があります。確実にするなら、StatGrid の stats や Compare の rows をMarkdownの表に変換する処理を書くか、MDXを一度レンダリングしてからテキスト化するかのどちらかです。前者は部品の中身まで伝わるため、次の改修候補にしています。全文版を出すなら、本文の抽出品質を一度は目で確認し、月に1回程度は行頭 < の行数を数えて増えていないかを見ます。

アクセス許可とは別に管理する

llms.txtにリンクを書いても、認証やrobots.txtのアクセス方針が変わるわけではありません。検索用botと学習用botの扱いは用途別のクローラー解説で確認します。

逆に、クローラー側がllms.txtをどう扱うかは、確認した範囲では書かれていません。2026年9月14日に4社のクローラーの公式ページを読んだ時点では、他サイトのllms.txtを読む、尊重する、引用に使うという記述はありませんでした。

事業者確認したクローラー他サイトのllms.txtへの言及
OpenAIGPTBot、OAI-SearchBot、ChatGPT-Userなし
AnthropicClaudeBot、Claude-User、Claude-SearchBotなし
PerplexityPerplexityBot、Perplexity-Userなし
GoogleGoogle-Extendedなし

OpenAIとPerplexityのページには自社ドキュメントのllms.txtへの案内がありますが、それは自社の資料の案内です。クローラーが第三者のllms.txtをどう扱うかの説明ではありません。

codeagent.jpのrobots.txtは主要なAIクローラーを個別に列挙しています。同日に照合すると、Anthropicの現行ページに載っていない Claude-Web と anthropic-ai が残り、現行の Claude-User と Claude-SearchBot は列挙されていませんでした。列挙していないbotには User-agent: * のグループが適用されます。列挙した一覧は古くなるため、公式ページと定期的に照合します。

社内資料や秘密情報の保護をrobots.txtだけに任せることも避けます。公開してよい資料だけを案内し、アクセス制限が必要な資料には認証を使います。

観察記録:Search Consoleで見えた取得

インデックス登録状況の「クロール済み・インデックス未登録」に、/llms.txt と /llms-full.txt が含まれていました。URL検査で開くと、どちらにも前回のクロールが記録されていました。

この観察から言えるのは2つです。Googlebotが両ファイルを取得していること、そして検索インデックスには登録されていないことです。llms.txtはサイトマップに載せていませんが、取得されていました。どの経路で見つかったかは確認していません。案内ファイルとして使う目的では、未登録であることを不具合として扱っていません。

言えないのは、AIサービスに読まれた、引用されたということです。Search Consoleが示すのはGooglebotの取得です。GPTBotやClaudeBot、PerplexityBotが /llms.txt を取得したかは、サーバー側でUser-Agent別のアクセスログを取らないと分かりません。codeagent.jpはCloudflare Pagesで配信していて、この時点では生ログを集計する仕組みを持っていなかったため、観測できていません。

「配信できた」と「効果があった」を分ける

Googleの説明では、AI機能への掲載に特別なAI用テキストファイルは不要です。llms.txtが無いことだけで検索上の不備とは判断できません。

「取得された」「読まれた」「効いた」は別の事実です。前の節の観察で言えるのは最初の一つだけです。llms.txtの有無だけを変えた比較もしていないため、効果は切り出せません。効果を断定している説明を見たら、その根拠が観測データなのか、提案文の言い換えなのかを確認してください。

導入を評価するなら、まず「この案内を渡された利用者が必要な資料へ到達できるか」を試します。質問、使用したファイル、参照されたページ、誤った参照を記録してください。これは案内の使いやすさの試験です。自然検索やAI検索の引用増加を示す試験とは別です。

アクセスログの取得も、ページが引用された証拠ではありません。チェッカーのllms.txt欄も配信状況の参考表示で、加点対象にはしていません。

観測できる状態に近づける

codeagent.jpが両ファイルを置いているのは、記事コレクションから生成していて手作業の保守が小さいためで、効果を確認したからではありません。全文版の抽出品質という保守課題も残っています。観測できていないものについては、次の順で記録を増やす予定です。

  1. User-Agent別のアクセスログを取る。 Cloudflareのログ機能か、/llms.txt へのリクエストをWorkersで数える仕組みを足します。AIクローラーが取得したかどうかは、これでしか分かりません。
  2. AI検索での引用を定期的に手動で確認する。 サービス名、日付、質問文、引用URLを残します。llms.txtの有無との因果は分かりませんが、そもそも引用されているかの基準線になります。記録の取り方はAI検索向けのサイト点検と共通です。
  3. robots.txtのUser-Agent一覧を公式ページと照合する。 半年に1回を目安に、各社のページと突き合わせます。
  4. Search Consoleで前回のクロールを定点で見る。 /llms.txt の記録が更新され続けていれば、Googlebot側の取得は続いています。

必要性と保守コストを比べ、使い道がある場合に導入してください。

関連して読む

この記事の情報・検証メモ
Tags
公開日
情報確認
参考リンク
7件
更新性
長く使える
更新管理

仕様・料金・提供範囲が変わりやすいテーマは、公開日・更新日・情報確認日を分けて管理します。 導入前には必ず記事末尾の一次情報と公式ドキュメントで最新状況を確認してください。

検証メモ
codeagent.jp 本番 /llms.txt・/llms-full.txt(curl、2026-09-14) Google Search Console(インデックス登録とURL検査の画面、2026-09-14) Astro 静的エンドポイント src/pages/llms.txt.ts / llms-full.txt.ts
Primary sources

一次情報・参考リンク

About the author
SHAYOUWORLD

日本の公共データAPIを使うMCPサーバーを作って公開し、ローカルLLMを自分のGPUで測った記録を、一次情報・検証ログ・失敗例とあわせて整理します。