ダブルファクトリーのオフィシャルサイトで、AIOの基盤をつくってみた ── Astro+WordPress構成で入れたllms.txtとJSON-LDの記録
前回の記事「AIブランディングとは? AIに会社の信用を正しく伝えるために、いま企業ができること」で、AIブランディングという考え方の全体像を書きました。
その中で、ダブルファクトリーでも自社サイト(double-factory.co.jp)で対応を進めていると触れましたが、抽象的な話だけで終わらせるのはあまり誠実ではないと思います。
今回はもう1歩踏み込んで、AIブランディングを実現するための下地=AIO(AI Optimization)の基盤を、自社サイトで実際にどう構築したかの記録を公開します。
うまくいったところだけでなく、途中で詰まったところ、原因が分からず数十分止まったところも、記事中に「備考」枠として囲んで残しています。同じ場所で足止めしないための地図として読んでいただければ。
この記事で答える問い
- AIOの基盤構築とは、具体的に何をどこまでやるのか
- なぜ、コンテンツ制作より先に基盤を整えるべきなのか
- Astro+WordPress+Cloudflare Pages の構成で、どう入れたのか
- どこで詰まりやすいのか
- 自社で難しい場合、外部に依頼できるのか
結論:AIOは「基盤 → コンテンツ」の順で組む
先に、実装した2日分を要約します。
- Day1:
llms.txt(会社情報の目次役ファイル)と、会社そのものを説明するJSON-LD(Organization / LocalBusiness / WebSite)を全ページに埋める - Day2:記事詳細ページに、記事1本ずつを説明するJSON-LD(
BlogPosting/BreadcrumbList)を追加。あわせてサイトマップの staging ドメイン混入を修正
技術としては特別に難しくありません。ダブルファクトリーでも実質2日で通り抜けました。ただし、コードが動く前提の話です。実際には Cloudflare が反映してくれなかったり、staging ドメインが本番のJSON-LDに混じっていたり、ドキュメントに書かれていない詰まりどころがいくつも出てきます。以下、順番に書きます。
そもそも、なぜコンテンツより先に「基盤」から整えるのか
結論:AIが読み取れないサイトにいくらコンテンツを積んでも、AIの回答には反映されないためです。順番として、基盤 → コンテンツで組むのが結果的に一番早い。
背景:ここ半年ほどの Google の検索アップデートで、ダブルファクトリーの周辺でも変化を観察しています。目立つ傾向は次の2つです。
- 狙ったキーワードだけで書いた「順位取り記事」の落ち込み:他社サイトを見にいくと、いわゆるHow-to系のまとめ記事、独自の観察や一次情報がなく既存記事の並べ替えに近いもの、が明確に沈んでいます。以前は上位に居たページが、いまは AI Overview に要約だけ吸われて、青リンクとしてはクリックされない位置に下がっている、というパターンです。
- 一次情報・実装ログ・実測データが入った記事の粘り:一方で、自分たちで手を動かした結果や、失敗を含む経過を書いた記事は、順位も維持されるか上がっています。AI Overview からもむしろ引用されやすい。今回のような「弊社サイトで実装してみた」タイプの記事は、この後者に含まれます。
つまり、AIとGoogleが求めているのは、量ではなく引用に値する固有の情報です。それを AI に伝える経路がJSON-LDやllms.txtなどの基盤なので、コンテンツを積む前にまず経路を通すのが、遠回りに見えて近道になります。
主張:ダブルファクトリーとしては、この順番を強めに推しています。AI対策の意味をしっかり把握したうえで、まず基盤を構築する。そのあと、基盤に乗せるコンテンツを展開していく。順番が逆だと、コンテンツを書いてもAIに拾われず、労力の割に成果が出ません。
現在のダブルファクトリーサイトの構成 ── Astro + WordPress + Cloudflare Pages
結論:フロントを Astro で静的HTMLに書き出し、バックエンドを WordPress(ヘッドレス運用)、配信を Cloudflare Pages に載せています。この構成は AIO の観点で有利です。
なぜ有利か:AIクローラーやGooglebotは、JavaScript実行前の初期HTMLを最優先で読みます。Astro は「デフォルトでHTMLに書き出し、必要なところだけJSを部分的に足す」思想のフレームワークなので、AI/Google が読む段階で本文と構造化データが既にHTMLに乗っています。SPA型で本文を後から JS で描くサイトに比べ、AIの読み取り漏れが起きにくい。
編集運用:本文と画像の管理はこれまで通り WordPress の管理画面で行い、Astro のビルド時にWP APIから取得してHTMLに焼き付ける形にしています。編集者にとっての操作感は WordPress のまま、AI にとっての読みやすさは Astro の静的HTMLで担保する、というハイブリッドです。
弊社の場合
- フロント:Astro
- バックエンド:WordPress(
test-doublefactory.siteを staging として利用、本番ドメインへは Cloudflare 側で置換)- 配信:Cloudflare Pages(master マージで自動ビルド → 本番公開)
- 共通JSON-LDコンポーネント:
src/components/meta/JsonLd.astro- サイトマップ配信:Cloudflare Functions の
functions/_middleware.jsで WP 生成のXMLを中継
Day1でやったこと① ── llms.txt:会社情報を書いた、たった1つのテキストファイル
やること:llms.txtという小さな Markdown ファイルを1つ書いて、サイトのルート(/llms.txt)に置きます。それだけです。
なぜ有効か:llms.txtは、生成AIのクローラーが会社情報を集めるときの「目次役」として提案されているファイルです。仕様は llmstxt.org にあり、2026年時点ではまだ提案段階で、すべてのAIが必ず読むわけではありません。それでも、読める側のAIには読ませておこう、という位置づけです。書くコストがほぼゼロなので、置かない理由がありません。
期待値の置き方:ただ、正直に書くと、このファイルを置いたからといってChatGPTの回答が急に変わるものではありません。「読める側のAIが会社情報を集めやすくする」ための、控えめな準備です。ここは大げさに期待しないほうがよさそうです。
弊社の場合Astro プロジェクトの
public/llms.txtに配置。見出しは5つに絞りました。
- 会社の紹介(正式社名・所在地・代表者・設立年)
- 事業内容(Web制作/動画制作/ブランディング/AI活用支援)
- 主要サービスページ(各サービスへの絶対URL)
- ブログ・実績・お知らせ(一覧ページへの絶対URL)
- 会社概要・問い合わせ(company・contact への絶対URL)
実際の中身は、次のような Markdown を数百字並べただけです。
# ダブルファクトリー株式会社 > 大阪を拠点にWeb制作・動画制作・ブランディング・AI活用支援を行う会社です。 ## 会社概要 - 正式社名: ダブルファクトリー株式会社 - 本社所在地: 大阪府大阪市 - 事業内容: Web制作、動画制作、ブランディング、AI活用支援 - 公式サイト: https://double-factory.co.jp/ ## 主要サービス - コーポレートサイト制作: https://double-factory.co.jp/service/website/ - ブランディング: https://double-factory.co.jp/service/branding/ - AI活用・AIブランディング: https://double-factory.co.jp/service/ai-branding/ ## ブログ・実績・お知らせ - ブログ: https://double-factory.co.jp/blogs/ - 実績: https://double-factory.co.jp/works/ - お知らせ: https://double-factory.co.jp/news/ ## 問い合わせ - お問い合わせ: https://double-factory.co.jp/contact/作業時間は30分弱でした。
Day1でやったこと② ── 会社情報のJSON-LDを、全ページに埋め込む
やること:会社そのものを説明する構造化データ(JSON-LD)を、全ページの<head>に共通で埋め込みます。最初は Organization、LocalBusiness、WebSite の3タイプで十分です。
なぜこの3つに絞ったか:JSON-LDには書けるタイプがたくさんあります(会社、店舗、記事、商品、レビュー、イベント、FAQ、動画など)。最初から全部入れると設計が重くなるので、まずは「会社そのもの」を説明する最小セットに絞りました。Organization は会社情報、LocalBusiness は大阪本社の拠点情報、WebSite はサイト全体の情報です。この3つを一組にすれば、AIや検索エンジンに「うちの会社はこういう会社で、こういう拠点で、こういうサイトを持っている」という基本情報がひととおり伝わります。
実装で仕込んだこと:3つを@graphという書き方でひとつのブロックにまとめ、それぞれに@idという識別子を振っておきました。@idを仕込む理由は、次の Day2 の話につながります。あとで記事ページに BlogPosting を追加するときに、「著者はこの会社(@id参照)」「発行元はこの会社」と、たった1行で引き回せるからです。
結論:会社情報のJSON-LDは、この3タイプ+@id仕込みが、これから拡張するときに一番楽になる置き方でした。逆にここで@idを省くと、Day2以降で著者・発行元をベタ書きすることになり、修正コストが膨らみます。
弊社の場合
- コンポーネント:
src/components/meta/JsonLd.astroを新設、全レイアウトから読み込み@graphに Organization / ProfessionalService(大阪本社の LocalBusiness)/ WebSite の3ノードを格納- それぞれ
@idを#organization/#localbusiness/#websiteで命名- 反映確認は リッチリザルトテスト にトップURLを貼り、3タイプすべてが緑バッジで表示されるかを目視
実際に全ページの
<head>に送っている中身は、次のような形です。{ "@context": "https://schema.org", "@graph": [ { "@type": "Organization", "@id": "https://double-factory.co.jp/#organization", "name": "ダブルファクトリー株式会社", "alternateName": "Double Factory Inc.", "url": "https://double-factory.co.jp/", "logo": "https://double-factory.co.jp/images/logo.svg", "description": "大阪を拠点にWeb制作・動画制作・ブランディング・AI活用支援を行う会社。", "sameAs": [ "https://www.facebook.com/doublefactory.inc", "https://www.instagram.com/double_factory_inc/" ] }, { "@type": "ProfessionalService", "@id": "https://double-factory.co.jp/#localbusiness", "name": "ダブルファクトリー株式会社 大阪本社", "parentOrganization": { "@id": "https://double-factory.co.jp/#organization" }, "url": "https://double-factory.co.jp/company/", "address": { "@type": "PostalAddress", "addressCountry": "JP", "addressRegion": "大阪府", "addressLocality": "大阪市" }, "areaServed": "JP" }, { "@type": "WebSite", "@id": "https://double-factory.co.jp/#website", "url": "https://double-factory.co.jp/", "name": "ダブルファクトリー株式会社", "publisher": { "@id": "https://double-factory.co.jp/#organization" }, "inLanguage": "ja" } ] }ポイントは3つあります。
@graphで3ノードをひとまとめにしていること、それぞれに@idを振っていること、ProfessionalServiceのparentOrganizationとWebSiteのpublisherで会社ノードを@id参照していること。この参照の仕組みが、Day2でBlogPostingのauthor/publisherを1行で書けるようになる下地です。
Day2でやったこと ── 記事ページに BlogPosting と BreadcrumbList を追加する
やること:ブログや事例の詳細ページに、記事1本1本の情報(BlogPosting)とパンくず階層(BreadcrumbList)を、Day1で入れたJSON-LDブロックに追加します。
なぜ入れるか:会社情報のJSON-LDだけだと、「この記事の著者は誰か」「いつ公開・更新したのか」「サイト構造のどこに位置するか」が伝わりません。BlogPostingを入れると、記事情報がAIの回答や検索結果に正確に反映されやすくなります。BreadcrumbListを入れると、Googleの検索結果のURL部分が「ホーム > ブログ > 記事タイトル」の階層表示に変わり、クリック前に伝わる情報量が増えます。
実装で気をつけた3点:
一つ目は、公開日・更新日のフォールバック。ダブルファクトリーの場合、公開日・更新日は WordPress 側の Yoast SEO が返すフィールド(opengraphPublishedTime、opengraphModifiedTime)から引きますが、Yoast 未設定の古い記事では空文字が返ってきました。空だった場合は投稿日(記事本体の date)にフォールバックする分岐を入れて、記事によって出たり出なかったりしないようにしました。
二つ目は、著者と発行元の@id参照。BlogPostingは毎回authorとpublisherを書く必要がありますが、記事ごとに会社情報を書き直すのは冗長です。Day1で会社情報のJSON-LDに@idを振っておいたおかげで、「著者はこの会社(@id参照)」の1行で済みました。将来、著者を担当ライターの個人名(Person タイプ)に切り替えたくなっても、@idの付け替えだけで対応できます。
三つ目は、stagingドメインの置換(詳細は下の備考枠)。
結論:BlogPostingとBreadcrumbListの追加は、上の3点さえ押さえれば難しくありません。逆に、この3点を踏まないと、せっかくの構造化データが検索側で無視されます。
弊社の場合
- JsonLd.astro に
article/breadcrumbsプロップを追加、BlogPostingとBreadcrumbListを条件付きで@graphに追加/blogs/[slug]と/news/[slug]のレイアウトで Yoast SEO のフィールドから組み立て、JsonLdに渡すauthorとpublisherは Day1 で振った#organizationを@id参照- staging → 本番ドメインの置換は JsonLd 側で1関数(1〜2行)を通す
実際に記事詳細ページで追加している中身は、次のような形です(
@graphに足す2ノード分)。{ "@type": "BlogPosting", "@id": "https://double-factory.co.jp/blogs/ai-branding/#article", "isPartOf": { "@id": "https://double-factory.co.jp/#website" }, "mainEntityOfPage": "https://double-factory.co.jp/blogs/ai-branding/", "headline": "AIブランディングとは? AIに会社の信用を正しく伝えるために、いま企業ができること", "image": "https://double-factory.co.jp/wp-content/uploads/ai-branding-og.jpg", "datePublished": "2026-09-07T09:00:00+09:00", "dateModified": "2026-09-05T18:00:00+09:00", "author": { "@id": "https://double-factory.co.jp/#organization" }, "publisher": { "@id": "https://double-factory.co.jp/#organization" }, "inLanguage": "ja" }, { "@type": "BreadcrumbList", "@id": "https://double-factory.co.jp/blogs/ai-branding/#breadcrumb", "itemListElement": [ { "@type": "ListItem", "position": 1, "name": "ホーム", "item": "https://double-factory.co.jp/" }, { "@type": "ListItem", "position": 2, "name": "ブログ", "item": "https://double-factory.co.jp/blogs/" }, { "@type": "ListItem", "position": 3, "name": "AIブランディングとは?" } ] }
authorとpublisherが Day1 の#organizationを@idで参照しているので、会社情報を書き直す必要がありません。将来、著者を担当ライター個人(Person)に切り替えたくなっても、@idの付け替え1行で済みます。備考:stagingドメインが本番のJSON-LDに紛れ込む
Astro のビルド時に WordPress の staging から取ってくる関係で、記事画像URLやパンくずURLに
test-doublefactory.siteが混じります。そのまま出すと Google に「本番の構造化データではない」と判定されて評価されません。対応はJSON-LDに渡す前に本番オリジンへreplaceAllで置換する1〜2行の処理を挟むだけ。リッチリザルトテストで staging 文字列が残っていないかを目視すれば確認できます。備考:サイトマップが「10件のエラー」で全滅していた
Day2直前、Search Console に
/sitemap.xmlを送信したところ検出URLがゼロ。原因は Cloudflare Functions のfunctions/_middleware.jsの置換ロジックが「.xmlで終わるURLだけを書き換える」条件になっており、子サイトマップ内の記事URL(末尾スラッシュ)と画像URLが staging のまま Googlebot に返っていたことでした。条件を外してreplaceAllに変えるだけで解消。順番として、実はllms.txtやJSON-LDより先にサイトマップの中身を見たほうが早い、というのが後から見た反省です。
ダブルファクトリーの現時点の結論 ── まず基盤、それからコンテンツ
結論:ここまでの Day1 / Day2 を通して確信したのは、「AI対策はしっかりと意味を把握したうえで、まず基盤を構築する。そのあと、その基盤に乗せるコンテンツを展開していく」という順番の重要性です。
基盤ができていないサイトでコンテンツを大量に書いても、AIから見ればまだ「読める材料」が揃っていない状態です。逆に、基盤(llms.txt/JSON-LD/サイトマップ)が整っていれば、後から公開する1本1本の記事が、AI回答と検索結果の両方に順当に効き始めます。
順位が下がるパターンと下がらないパターンを他社サイトも含めて観察していますが、ここ最近の傾向は明確です。一次情報や実装ログ、独自の観察が入った記事は残り、既存記事の要約に近いものは沈んでいます。AIとGoogleが求めているのは、量ではなく、引用に値する固有の情報。それを AI に確実に届けるためのレールが AIO の基盤側にある、という理解でいます。
ダブルファクトリーとしても、Day3以降は、この基盤の上にケーススタディ・実測データ・実装ログを順次乗せていく方針です。
結果はいつ出るのか ── 1週間ではなく、3ヶ月で見る
結論から:Google 側の反映は数日から2週間、Google AI Overview や Gemini で会社が引用されるようになるのは1〜3ヶ月です。1週間で判断してはいけない、というのが今回の実感です。
なぜ時間がかかるか:Google Search Console のクロール自体は、自然に待って3〜7日、「インデックス登録をリクエスト」を打っても1〜2日はかかります。BlogPosting や BreadcrumbList が検索結果のリッチ表示に反映されるのは、クロールが終わった後の数日〜2週間。AI Overview や Gemini のように、Googleの理解がAI回答に組み込まれる領域は、さらに1〜2ヶ月遅れて動きます。継続的な更新があって、3ヶ月目くらいから手応えが出始めるのが現実的です。
AI回答エンジン別の体感:Perplexity は反応が早く、数日から1週間で公式サイトの説明や llms.txt が回答に混ざり始めます。ChatGPT の検索、Gemini、Claude は1ヶ月前後を見ておくのが妥当です。ダブルファクトリーも1週間で判定するのはやめて、少なくとも1ヶ月、目安は3ヶ月のスパンで、同じ質問を繰り返し投げて回答の変化を記録していく予定です。
その場で確認できる方法:GoogleのリッチリザルトテストにページURLを貼れば、埋め込んだ構造化データが認識されているかをその場で見られます。Organization、LocalBusiness、WebSite、BlogPosting、BreadcrumbList のバッジが緑で並べば、少なくとも技術的には反映されていると判断できます。
再結論:数日で判断しない。3ヶ月で見る。その間、同じ質問をAIに定期的に投げて、回答の変化を残しておくのが、後から効果を語れる唯一の方法です。
よくある質問(FAQ)
Q. AIOの基盤構築を、外部に依頼することはできますか?
もちろんできます。ダブルファクトリーでは、今回の記事で書いた Day1 / Day2 の基盤構築を、他社サイトへの導入サービスとして提供しています。サイト構成(WordPress単独、Astro+WordPress、Next.js、その他)に合わせて、llms.txt の設計、JSON-LDの設計と実装、サイトマップの健全性チェック、リッチリザルトテスト通過までを一括で対応します。まずは現状の診断からでもお受けできます。お問い合わせからご連絡ください。
Q. Day1とDay2、どちらから着手すべきですか?
Day1(llms.txtと会社情報のJSON-LD)が先です。Day2で記事ページに BlogPosting を入れるとき、著者・発行元を Day1 で振った @id で参照するのが最も楽な作り方だからです。Day2を先にやってしまうと、著者・発行元をベタ書きすることになり、あとで会社情報を変更するときの修正コストが大きくなります。
Q. llms.txtを置くだけで、ChatGPTの回答は変わりますか?
このファイル単独では大きくは変わりません。llms.txtは2026年時点でまだ提案段階の仕様で、すべての生成AIが読んでいるわけではありません。会社情報の整理、構造化データの追加、サイトマップ整備、外部媒体での発信を組み合わせて、AIが会社情報にたどり着きやすい状態を作ることが、実質的な効果につながります。
Q. JSON-LDを入れるとして、最初にどのタイプから始めればよいですか?
コーポレートサイトなら、まず Organization、LocalBusiness(拠点があるなら)、WebSite の3つで十分です。ブログや事例ページを持っているなら、BlogPostingとBreadcrumbListを追加するのが自然な流れになります。EC や飲食など、業種に固有の情報があれば Product、Menu、Review などを足していきます。全部を最初から入れる必要はありません。
Q. Cloudflare Pages のデプロイが反映されないときは、どうすればよいですか?
Cloudflare のダッシュボードから該当プロジェクトの Deployments 画面を開き、最新の Production 行の右側から「デプロイを再試行」を押します。数十秒から数分で本番に反映されるはずです。ロールバックのボタンを押すと現在の変更が消えてしまうので使いません。Preview の行を再試行しても本番反映はされないので、必ず Production 行を選びます。
Q. Astro+WordPress以外の構成でも、同じことはできますか?
できます。Next.js、Nuxt、Gatsby、あるいは WordPress 単独でも、llms.txt はルートに配置するだけ、JSON-LD は <head> に埋めるだけ、サイトマップは配信XMLに本番ドメインが並ぶことを確認するだけ、と本質は共通です。構成によって触るファイルが変わるだけで、やること自体は同じです。
Q. 実装から結果が出るまで、どれくらい待てばよいですか?
Google のクロール反映は3〜7日、リッチリザルトの反映は数日から2週間、Google AI Overview や Gemini での引用は1〜3ヶ月が目安です。Perplexity は数日から1週間で反応が出やすい印象です。1週間で結論を出そうとすると変化が拾えないので、少なくとも1ヶ月、通常は3ヶ月のスパンで見ておくのが現実的です。
最終更新:2026-09-08 執筆:壽山博之(ダブルファクトリー株式会社 代表取締役)








