結論から書きます。AI 検索に引用されたいなら、FAQ を「人間が読める文章」にするだけでは足りません。質問と回答の対応を、機械がそのまま取り出せる形——FAQPage の構造化データ(JSON-LD)——で渡しておく必要があります。
この記事では、schema.org の FAQPage とは何かという基本から、コピペで動く最小の JSON-LD、そして MDX の frontmatter に書いた faq を起点に本文表示と JSON-LD を一括生成する設計までを、実装一次情報として示します。私たち自身がこのサイトで使っている仕組みをそのまま分解する内容なので、概論ではなく「動くコード」を持ち帰ってもらえるはずです。AI 検索全体の戦略は AI モード時代の SEO とは で整理していますが、本記事はその中の「構造化データをどう実装するか」に絞って深掘りします。
結論:AI に引用されるには FAQ を「機械可読」にする
AI 検索や AI Overviews は、複数のページを横断して要約し、ユーザーの質問に答えます。このとき AI が探しているのは「この質問に対する、この回答」という対応関係です。本文に Q&A をきれいに書いていても、見出しと段落の組み合わせだけでは、どこからどこまでが 1 つの質問と回答なのかを機械が確実に切り出せるとは限りません。
FAQPage 構造化データは、この対応関係を明示的に宣言する仕組みです。「これは質問」「これがその回答」というラベルを、ページの裏側に JSON-LD として埋め込みます。人間が見る画面は変わりませんが、クローラーや LLM ベースの検索は、曖昧さなく Q&A の単位を取り出せるようになります。
やることは大きく 3 つに分かれます。本文に表示する FAQ を用意すること、その同じ内容から FAQPage の JSON-LD を生成すること、そして本文と JSON-LD が一致した状態を運用で崩さないこと。3 つ目が抜けると、コンテンツ更新のたびに不一致が生まれてガイドライン違反になりがちなので、後半で「frontmatter を単一の真実にする」設計として解決します。
FAQPage 構造化データとは何か(schema.org の基本)
FAQPage は schema.org が定義する型の 1 つで、「運営者が用意したよくある質問と、その回答の一覧」を表します。重要なのは主語です。FAQPage は、ページの運営者が答えを提供している Q&A に使います。ユーザー同士が質問・回答を投稿し合う掲示板のようなページは QAPage という別の型を使い分けます。
FAQPage の構造はシンプルで、入れ子は二段だけです。FAQPage が mainEntity として複数の Question を持ち、各 Question が 1 つの acceptedAnswer(Answer 型)を持ちます。
FAQPage:ページ全体が「FAQ ページである」ことを宣言するルートQuestion:1 つの質問。nameに質問文(見えている文言そのまま)acceptedAnswer(Answer):その質問に対する回答。textに回答本文
構造化データの記法には JSON-LD、Microdata、RDFa の 3 つがありますが、Google が推奨しているのは JSON-LD です。HTML のマークアップと分離して <script type="application/ld+json"> に一括で書けるため、本文の構造に縛られず、テンプレートから生成しやすいという実務上の利点があります。本記事も JSON-LD 前提で進めます。
AI 検索・AI Overviews が FAQPage をどう使うか
ここで 1 つ前提を共有しておきます。FAQPage の「青色リッチリザルト(検索結果にアコーディオンで質問が並ぶ表示)」は、近年その表示対象が大きく絞られています。現在この見た目が出るのは、主に政府系・医療系など一部のサイトに限られると案内されており、一般的な事業会社のブログでアコーディオンが出ることはほぼ期待できません。
「では入れる意味は薄いのでは」と思われるかもしれませんが、逆です。表示装飾としての価値が下がった一方で、機械可読なデータとしての価値は AI 検索の普及でむしろ上がっています。AI Overviews や AI モードのような生成型の検索は、ページを要約して回答を作る過程で、明確に構造化された Q&A を「そのまま使える素材」として扱えます。質問文がユーザーの実際の検索クエリに近く、回答が簡潔で自己完結していれば、引用される確率はそのぶん上がります。
つまり FAQPage は、見た目のリッチリザルトを狙う施策から、AI に引用されるための機械可読化施策へと役割が移ったと捉えるのが正確です。だからこそ、質問文は「実際に検索されそうな言い回し」で書き、回答は前後の文脈に依存せず単体で意味が通る長さにまとめておくことが効いてきます。
JSON-LD の最小実装サンプル(コピペで動く)
まずは依存関係なしで動く最小形です。HTML の <head> か <body> 内のどこかに、次の <script> を置けば FAQPage として認識されます。
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "FAQPage 構造化データを入れると検索順位は上がりますか?",
"acceptedAnswer": {
"@type": "Answer",
"text": "FAQPage 自体はランキング要因ではありません。構造化データはページの内容を機械が正確に読み取るための補助であり、それだけで順位が上がる保証はありません。"
}
},
{
"@type": "Question",
"name": "ページに書いていない Q&A を JSON-LD だけに含めてもよいですか?",
"acceptedAnswer": {
"@type": "Answer",
"text": "避けてください。構造化データの内容は、ユーザーに見える本文と一致している必要があります。本文にない Q&A を JSON-LD にだけ書く隠しコンテンツはガイドライン違反です。"
}
}
]
}
</script>押さえるべき点は 4 つです。@context は https://schema.org 固定。ルートの @type は FAQPage。mainEntity は Question の配列で、最低でも一組あれば成立します。そして各 Question には acceptedAnswer を 1 つだけ持たせます。
Answer の text には簡単な HTML(<p> <a> <ul> <li> など)を含められますが、本文に見えている内容と食い違わないことが絶対条件です。見えていない情報を text に盛り込むのは避けてください。回答に外部リンクや箇条書きを入れたい場合は、HTML をエスケープせずそのまま文字列として入れる形になります(JSON の文字列としてエスケープが必要な引用符などには注意します)。
この最小形でも有効ですが、毎ページ手書きすると本文との不一致が必ず起きます。次にその不一致を構造的に消す設計へ進みます。
MDX frontmatter の faq から JSON-LD を自動生成する設計(Velite / Next.js)
私たちがこのサイトで採っているのは、frontmatter の faq を「単一の真実(single source of truth)」にし、そこから本文表示と JSON-LD の両方を生成するやり方です。FAQ を一箇所にしか書かないので、定義上、本文と構造化データは一致します。
まず記事の frontmatter に faq を配列で持ちます。
---
slug: faqpage-structured-data-for-ai-search
title: FAQPage 構造化データの書き方
faq:
- question: "FAQPage 構造化データを入れると検索順位は上がりますか?"
answer: "FAQPage 自体はランキング要因ではありません。正確に読み取られるための補助です。"
- question: "ページに書いていない Q&A を JSON-LD だけに含めてもよいですか?"
answer: "避けてください。構造化データは本文と一致している必要があります。"
---次に、これをコンテンツレイヤーでスキーマ化します。Velite を使う場合、コレクション定義で faq を検証つきのフィールドとして宣言しておくと、型の崩れをビルド時に弾けます。
// velite.config.ts
import { defineCollection, defineConfig, s } from "velite";
const faqItem = s.object({
question: s.string().max(120),
answer: s.string().max(800),
});
const insights = defineCollection({
name: "Insight",
pattern: "insights/**/*.mdx",
schema: s.object({
slug: s.string(),
title: s.string().max(120),
faq: faqItem.array().min(2).max(4).optional(),
// ...他のフィールド
}),
});
export default defineConfig({
collections: { insights },
});ここで min(2) max(4) のように件数や文字数を縛っておくと、回答が長くなりすぎる・質問が 1 つしかないといった運用上の崩れを、執筆段階のビルドエラーとして検知できます。スキーマで縛ることが、そのまま構造化データの品質ガードになります。
最後に、faq から JSON-LD を組み立てるユーティリティを用意します。文字列を素朴に連結するのではなく、オブジェクトを JSON.stringify する形にすると、引用符などのエスケープを安全に任せられます。
// lib/faq-jsonld.ts
type FaqItem = { question: string; answer: string };
export function buildFaqJsonLd(faq: FaqItem[]) {
return {
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: faq.map((item) => ({
"@type": "Question",
name: item.question,
acceptedAnswer: {
"@type": "Answer",
text: item.answer,
},
})),
};
}Next.js(App Router)では、この結果をページ側で <script> として描画します。dangerouslySetInnerHTML を使いますが、中身は JSON.stringify した自前データなので、ユーザー入力由来の XSS リスクはありません。
// app/insights/[slug]/page.tsx の一部
import { buildFaqJsonLd } from "@/lib/faq-jsonld";
export default function ArticlePage({ article }: { article: Insight }) {
const faqJsonLd = article.faq?.length ? buildFaqJsonLd(article.faq) : null;
return (
<>
{faqJsonLd && (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqJsonLd) }}
/>
)}
<article>{/* 本文 */}</article>
{/* 同じ article.faq を画面表示にも使う */}
<FaqSection items={article.faq} />
</>
);
}ポイントは、画面表示の FaqSection と JSON-LD の buildFaqJsonLd が、どちらも同じ article.faq を入力にしていることです。FAQ を編集したいときは frontmatter だけを直せばよく、本文と構造化データが自動で同期します。この「定義を 1 つにする」発想は、AI に正しく読ませる土台づくりという意味で AI 駆動開発でプロダクトを設計する考え方 とも通じます。コンテキストを一箇所に集約し、人間にも機械にも同じ事実を渡すという点では、CLAUDE.md の運用ベストプラクティス で扱う「プロジェクトの真実を一枚に集める」考え方と同じ系譜です。
やりがちな失敗:質問文と回答の不一致・隠しコンテンツ・重複
実装そのものは難しくありません。落とし穴はほぼ運用と内容側にあります。代表的な 3 つを挙げます。
1 つ目は本文と JSON-LD の不一致です。本文を後から書き換えたのに JSON-LD を直し忘れる、あるいは JSON-LD にだけ盛った質問が本文に存在しない、というケースです。これはガイドライン違反であり、リッチリザルト対象から外れる原因になります。前述の「frontmatter を単一の真実にする」設計を採れば、この問題は構造的に発生しません。逆に、本文に手書きの Q&A、JSON-LD は別管理、という二重管理は遅かれ早かれ破綻します。
2 つ目は隠しコンテンツです。ページ上に表示されていない FAQ を構造化データに含めることは禁止されています。「とりあえず検索クエリを大量に詰めておこう」とばかりに、画面に出ない質問を JSON-LD に羅列するのは典型的な NG パターンです。表示している内容と一致させる、という原則を徹底してください。
3 つ目は回答の重複と冗長です。同じ趣旨の質問を言い換えただけのものを並べたり、1 つの回答に複数の話題を詰め込みすぎたりすると、AI が「この質問に対する答え」として切り出しにくくなります。質問は実際に検索されそうな単位で 1 つずつ立て、回答は 1 つの問いに自己完結する形でまとめます。回答が長くなりすぎる場合は、本文の該当セクションへ誘導する一文を添え、回答自体は要点に絞るのが扱いやすい形です。
加えて、宣伝色の強い表現や、質問の体をなしていない見出し(「お問い合わせはこちら」だけ、など)を Question にするのも避けます。あくまで「ユーザーの疑問とその答え」という体裁を保つことが、引用されやすさにつながります。
リッチリザルトテストでの検証手順
実装したら必ず検証します。順番は次の通りです。
最初に Google のリッチリザルトテストに、公開済み URL かレンダリング後の HTML を入力します。FAQ が FAQPage として検出され、各 Question と acceptedAnswer が想定どおりに並んでいるかを確認します。ここでエラー(必須プロパティ欠落など)や警告(推奨プロパティ欠落)が出たら、JSON-LD 側を修正します。Next.js のようにクライアント描画が絡む場合は、ビューソースではなくレンダリング後の DOM に <script type="application/ld+json"> が存在するかを確認してください。
補助的にスキーママークアップ検証ツール(Schema Markup Validator)も使えます。こちらは Google 固有のリッチリザルト要件ではなく、schema.org の構文として正しいかを純粋にチェックする用途です。型の綴り間違いや入れ子の崩れを早期に見つけられます。
公開後は Search Console の拡張(構造化データ)レポートで、FAQ の検出数とエラー・警告の推移を継続的に見ます。テスト時点では通っていても、テンプレート変更や本文編集で後からエラーが混入することがあるためです。リッチリザルトテストが一度通れば終わり、ではなく、デプロイのたびに自動チェックへ組み込めると理想的です。CI で代表ページの JSON-LD をパースして @type と件数を検証するだけでも、不一致の事故はかなり減らせます。
FAQPage 以外に併用すべき構造化データ(TechArticle / Article)
FAQPage は記事の一部(末尾の Q&A)に対応する構造化データです。記事全体の文脈は、別の型で補完すると AI 検索に対してより正確な情報を渡せます。
技術解説記事であれば TechArticle、一般的な記事であれば Article を、ページ全体の @type として併用します。ここでは著者(author)、公開日(datePublished)、更新日(dateModified)、見出し(headline)、発行元(publisher)といった情報を渡します。とくに著者情報は E-E-A-T の観点で重要で、誰が書いた記事かを機械可読にしておくことは、AI 検索が信頼性を判断する材料になります。著者 Person と運営 Organization を sameAs で外部プロフィールに連結する設計は、E-E-A-T を構造化データで補強するで詳しく解説しています。
複数の構造化データは、別々の <script type="application/ld+json"> に分けても、1 つの @graph 配列にまとめても、どちらでも有効です。運用上は、記事全体を表す TechArticle/Article と、末尾の Q&A を表す FAQPage を併記し、それぞれを frontmatter の値(author・published・updated・faq など)から生成する形にしておくと、追加の管理コストなく一貫性を保てます。FAQ だけ構造化して記事本体を素のままにしておくのはもったいないので、最低限 Article 系の併用までは合わせて実装することをおすすめします。
まとめ
FAQPage 構造化データは、青色リッチリザルトを狙う装飾ではなく、AI 検索に正しく引用されるための機械可読化施策へと役割が変わりました。やるべきことは、本文に表示する FAQ を用意し、同じデータから JSON-LD を生成し、両者が一致した状態を運用で崩さないことの 3 つです。
実装の肝は、frontmatter の faq を単一の真実とし、Velite などのスキーマで件数・文字数を縛り、本文表示と JSON-LD を同じ入力から組み立てること。これにより不一致や隠しコンテンツといったガイドライン違反は構造的に起きなくなります。最後にリッチリザルトテストと Search Console で継続的に検証すれば、AI に引用される土台は整います。
構造化データの設計やコンテンツ基盤の実装でつまずいているなら、AI 検索全体の戦略を整理した AI モード時代の SEO とは も合わせてご覧ください。frontmatter からの自動生成や MDX コンテンツ基盤の設計を実プロジェクトに組み込みたい場合は、AI 駆動開発のクリエイティブスタジオである fixit の AI 駆動開発サービス にご相談ください。一次情報として動く実装まで、一緒に設計します。

