ReactのSEO
Reactはデフォルトでクライアントサイドレンダリングを行うため、クローラーはJavaScriptが実行されるまで空のシェルしか見ることができません。ここでは、Googleが実際にReactアプリをどのように処理するか、どのレンダリング戦略を選ぶべきか、そしてルーティング、メタデータ、レンダリングタイムアウトによる重複コンテンツの罠を修正する方法を説明します。
言語
React自体はSEOに悪いわけではありませんが、デフォルトのクライアントサイドレンダリングは問題です。標準の構成(CRA、Vite + React)では、サーバーは空のシェルを送信し、ブラウザがページを構築するため、クローラーはJavaScriptが実行されるまで何も見ることができません。GoogleはWeb Rendering Serviceを通じてReactをレンダリングできますが、レンダリングはキューに入れられ、遅延し、タイムアウトする可能性があります。Gary Illyes氏は、レンダリングのタイムアウトにより、ボイラープレートのみのページが残り、重複としてマークされることを示しています。AIクローラーのレンダリング契約はプロバイダーによって異なるため、CSRのみのコンテンツはカバレッジリスクを追加します。修正方法はレンダリング戦略です。SSRまたはSSG(最も簡単なのはNext.jsまたはRemix)を使用すると、コンテンツが初期HTMLに含まれ、hydrateRoot(createRootではなく)でハイドレーションされるため、サーバーとクライアントの出力が完全に一致します。次に、History APIルーティング、実際の<a href>リンク、正しいステータスコード、バージョンに適したメタデータを使用します。React 19では<title>/<meta>/<link>がネイティブにホイストされますが、それ以外の場合はreact-helmet-async(メンテナンスされていない元のreact-helmetは使用しない)を使用します。
TL;DR — ReactはSEOに悪いわけではありません — しかし、ほとんどのReactアプリの作り方自体が悪いのです。 デフォルトでは、Reactは訪問者のブラウザでページを構築するため、検索エンジンが 最初にURLを取得したとき、ほぼ空のページが返ってきます。Googleは通常、JavaScriptを実行して 空白を埋めることができますが、完成したHTMLを渡すよりも遅く、リスクが高くなります。 解決策は、サーバー側またはビルド時にページをレンダリングすることです — 通常はNext.jsのようなフレームワークを使います。
Reactが異なる理由
ほとんどのウェブサイト — 例えばWordPressブログ — は、検索エンジンに完全なページを送信します。
サーバーがHTMLを構築して、見出しも含めてすべてを送信します。標準的なReactアプリはその逆です。
サーバーはほぼ空のシェル(基本的に空の<div>)を送信し、その後ブラウザでJavaScriptが実行されて実際のページが構築されます。
これは洗練されたアプリのような体験には最適です。SEOにとっては問題です。なぜなら、 クローラーが最初にダウンロードするのはその空のシェルだからです。あなたのコンテンツはまだそこにありません — JavaScriptが実行された後にのみ表示されます。
GoogleはJavaScriptを実行できないのですか?
できます — Googleは舞台裏で実際の最新版Chromeを実行し、あなたのJavaScriptを実行して完成したページを確認できます。したがって、Reactコンテンツはインデックスされる可能性があります。 Evidence for this claim Googlebot uses an evergreen Chromium rendering engine and can execute JavaScript. Scope: Google Search; successful execution still depends on accessible resources and application behavior. Confidence: high · Verified: Google: JavaScript SEO basics
ただし、いくつかの問題があります:
- 遅延します。 Googleはレンダリングを後で、キューに入れられた別のステップで行います。 そのため、コンテンツが検索に表示されるまでに時間がかかることがあります。
- 失敗する可能性があります。 ページのコンテンツの読み込みが遅い場合、Googleのレンダラーは コンテンツが表示される前に諦めて、ほぼ空白のページをインデックスする可能性があります。
- 他のクローラーはさまざまです。 BingはJavaScriptの処理が信頼性に欠け、AIプロバイダーは 共通のレンダリング契約を公開していません。初期HTMLのみを取得するクローラーは、 デフォルトのReactアプリを空として見ることになります。
簡単な解決策
コンテンツをブラウザに届く前にHTMLに含めます。2つの方法があります:
- サーバーサイドレンダリング(SSR) — サーバーが各リクエストに対して完全なページを構築します。
- 静的サイト生成(SSG) — ページは事前に完成したHTMLにビルドされます。 Evidence for this claim React supports server rendering APIs and can be used by frameworks that generate HTML outside the browser. Scope: React server APIs; build-time generation is a framework/build-system capability rather than a React mode by itself. Confidence: high · Verified: React: Server APIs
どちらへの最も簡単な道はNext.jsです。これはReact上に構築されたフレームワークで、これを自動で行います。(Remixも良い選択肢です。)SSRまたはSSGを使用すると、Reactサイトはクローラーに完全なページを渡し、通常のウェブサイトと同じように検索エンジンに最適化されます。
他に注意すべき点
- 通常のURL(
/products)を使用し、ハッシュURL(/#/products)は使用しないでください — Googleは ハッシュ付きのURLを確実にインデックスできません。 - リンクは実際のリンク(
<a href>)にし、クリック可能な<div>にしないでください。 - 各ページに独自のタイトルと説明を設定し、ページが変更されたときに更新されるようにしてください。
より深いバージョン — Googleのレンダラーが実際にどのように機能するか、重複ページを作成するレンダリングタイムアウトの罠、レンダリング戦略の比較、Googleが見ているものをテストする方法 — をご希望ですか?詳細タブに切り替えてください。
TL;DR — ReactのSEO問題はReact自体ではなく、デフォルトのクライアントサイドレンダリングにあります。 CRAやVite + Reactは空のシェルを配信し、ブラウザでDOMを構築するため、 クローラーが取得する生のHTMLにはコンテンツがありません。GoogleはWebレンダリングサービス(常緑のChromium)経由でレンダリングできますが、レンダリングは別途キューに入れられ、遅延する可能性があり、タイムアウトする可能性があります — Gary Illyesは、ボイラープレートのみのページが残り、重複としてフラグ付けされるレンダリングタイムアウトを文書化しています。BingはJSのレンダリングがそれほど確実ではありません。AIクローラーのレンダリングはプロバイダーによって異なります。修正方法はレンダリング戦略です。SSRまたはSSG(最も簡単なのはNext.jsまたはRemix)により、コンテンツが初期HTMLに含まれます。サーバーレンダリングされたマークアップをハイドレーションする場合は、
hydrateRoot(createRootではなく)を使用し、サーバー/クライアントの不一致は抑制する警告ではなくバグとして扱います。次に、History APIルーティング(ハッシュURLではなく)と実際の<a href>リンクを使用します。<head>メタデータについては、React 19は<title>/<meta>/<link>をネイティブにホイストします。React 18または高度なニーズには、react-helmet-asyncを使用します(メンテナンスされていない元のreact-helmetは使用しないでください)。正しいHTTPステータスコードを設定します。SSRにランキングボーナスはありません — コンテンツを確実にインデックス可能にするだけです。一般的なレンダリングの仕組みについては、JavaScript SEOおよびヘッドレスCMSのトピックを参照してください。
ReactがSEOにとって難しい本当の理由
ReactはコンポーネントベースのJavaScriptライブラリであり、標準のまま(Create React App、Vite + React)ではクライアントサイドで実行されます。サーバーはほぼ空のドキュメント(有名なのは<div id="root"></div>だけ)とJavaScriptのバンドルを返し、ブラウザがそのJavaScriptを実行してDOMを構築します。これに対し、サーバーレンダリングされたページ(WordPress、Railsアプリ)では、完全なHTML — コンテンツ、見出し、リンク — が最初のレスポンスで届きます。
したがって、すべてを決定する質問は次のとおりです。JavaScriptが実行される前の生のHTMLには何が含まれているか? デフォルトのReactアプリの場合、答えは「ほとんど何もない」です。CRAアプリで右クリック → ソースの表示をすると、シェルは見えますが、コンテンツは見えません。これがまさにクローラーが最初のフェッチで取得するものです。
責任が実際にどこにあるかを正確に言うと、Reactライブラリ自体はCSR専用ではありません。React DOMには、クライアントレンダリング(createRoot)、サーバーレンダリング(ストリーミングおよび静的API)、ハイドレーションAPIが含まれており、ライブラリはこれらすべてをサポートしています。空のシェルの問題は、デフォルトのツールチェーン(Create React App、サーバーなしのVite + React)の特性であり、クライアントAPIのみを配線し、サーバーでHTMLをレンダリングするものは何もありません。ツールチェーンをNext.js、Remix、またはReact独自のサーバーレンダリングAPIに切り替えると、同じライブラリが最初のレスポンスで完全なHTMLを配信します。
これは、より広範なJavaScript SEO問題へのReact固有の適用です。一般的な障害モード(パリティ、インタラクション、状態、タイミング)については、そちらを参照してください。ここでは、Reactに固有の点とその修正方法に焦点を当てます。
Googleが実際にReactアプリを処理する方法
GoogleはJavaScriptを3つのフェーズで処理します:クロール → レンダリング → インデックス。GooglebotがURLをフェッチし、レンダリングされたDOMは後でWebレンダリングサービス(WRS) — Chromeと同じエンジンである常緑のChromium — によって構築され、その後、レンダリングされた出力がインデックスされ、そのリンクが抽出されます。 Evidence for this claim Google processes JavaScript pages through crawling, rendering, and indexing using its Web Rendering Service. Scope: Google Search rendering behavior. Confidence: high · Verified: Google: JavaScript SEO basics
重要なニュアンスは、レンダリングがいつ行われるかです。レンダリングはリソースを大量に消費するため、初期クロールとは別にキューに入れられます。Martin Splittはこの流れを明確に説明しています:「HTTPリクエストを行い、何かが返ってきます… いくつかの骨組みだけのHTMLで、それが行うのはJavaScriptをロードして実行することだけです。その後、このHTMLは… レンダリングに入ります。レンダリングはJavaScriptを実行します — ブーム!以前は存在しなかった多くのコンテンツが発生します。」 CSR Reactページの場合、その「ブーム」がページ全体です — そのレンダリングステップが実行されるまで、そのどれも存在しません。
持ち運ぶ価値のある注意点: 古い「2つのウェーブのインデックスモデル」に過度に依存しないこと。 Splitt自身もそれを撤回し、そのウェーブを*「過度な単純化」*と呼んだ。実用的な takeawaysは「正式なWave 2が定義されたタイミングで存在する」ということではなく、レンダリングが 明確で、延期可能で、失敗しうるステップであり、CSRはコンテンツの100%をその間違った側に置くということだ。
Reactアプリに特に影響するレンダラーの事実がさらに2つある:
- ステートレスである。 Googlebotはページ読み込み間で
localStorage、sessionStorage、または クッキーを保持しない。クライアント側の状態に依存するコンテンツやルーティングは クローラーには見えない。 - 諦めることがある。 レンダラーはタイムアウトを強制する。メインコンテンツの読み込みが遅い場合 — 大きなバンドル、APIコールのウォーターフォール — レンダリングはコンテンツが到着する前に完了し、 Googleは不完全なページをインデックスする。
レンダリングタイムアウトの罠(そしてなぜ重複を生むのか)
この失敗モードはほとんど明確に説明されておらず、Reactアプリにとって最も有害なものです。 Gary Illyesはそれを直接説明しました: 「私の受信箱には、中心となるコンテンツの読み込みに 永遠に時間がかかり、レンダリングがタイムアウトしたというメールがたくさんあります…そして 私たちはボイラープレートだけのページを大量に残されました。ボイラープレートだけでは、 それらのページは重複です。」
それがCSR Reactアプリにとって何を意味するかを考えてみましょう。ヘッダー、ナビ、フッターは 速く読み込まれるボイラープレートです。実際のページコンテンツ — 各URLをユニークにする部分 — は JavaScriptによって取得・レンダリングされ、読み込みが遅いです。レンダリングがタイムアウトします。 GoogleはすべてのURLでヘッダー+ナビ+フッターだけを残されます。今やすべてのページが 同一に見え、GoogleはそれらをSearch Consoleで互いの重複としてマークします。
Illyes自身の修正は実用的な部分です: 「コンテンツ(わずかなボイラープレートを含む)が最初に 読み込まれるようにjs呼び出しを再構築してみて、それが役立つかどうか確認してください。」 しかし、より 耐久性のある答えは、メインコンテンツをレンダリングステップに依存しないことです — つまり SSRまたはSSGです。
Reactのレンダリング戦略
これは最も影響力のある単一の決定です。SEOにとって、おおよそ最悪から最良へのオプション:
- CSR(デフォルトのReact)。 サーバーはシェルを送信し、ブラウザがすべてを構築します。コンテンツは レンダリングキューによって遅延され、タイムアウトにさらされます。SEOには最悪。 インデックスされたくない認証済みダッシュボードには問題ありません。 Evidence for this claim Client-only React rendering constructs UI in the browser; server rendering APIs produce HTML before browser hydration. Scope: React rendering mechanics; SEO impact depends on what the initial response contains. Confidence: high · Verified: React: hydrateRoot React: Server APIs
- プリレンダリング。 完全なSSRフレームワークなしのビルド時レンダリング —
react-snapや プリレンダーサービスなどのツールがアプリをクロールし、静的HTMLを保存します。軽量で、 よりシンプルでほぼ静的なサイトに適しています。 - SSG(静的サイト生成)。 デプロイ時に一度HTMLを構築し、静的ファイルとして提供します。 最速で、コンテンツは常に生のHTMLに存在します。高度に動的またはユーザーごとのコンテンツには 制限があり、大規模サイトではビルドが遅くなります。
- SSR(サーバーサイドレンダリング)。 サーバーがリクエストごとにReactを実行し、完全なHTMLを送信します。 コンテンツはクローラーに即座に利用可能で、常に新鮮です。Node.jsサーバーとわずかに高いTTFBが必要です。
- ハイブリッド/ISR(インクリメンタル静的再生成)。 静的ページをバックグラウンドで再生成するNext.jsの機能 — 静的な速度と定期的な新鮮さを兼ね備えています。
| 戦略 | 初期HTMLにコンテンツがあるか? | SEOリスク | 最適な用途 |
|---|---|---|---|
| CSR(生のReact) | いいえ | 最高 | ログイン済みダッシュボード、インデックスされないアプリ |
| プリレンダリング | はい(ビルド時) | 低 | 小規模でほぼ静的なサイト |
| SSG | はい(ビルド時) | 最低 | ブログ、ドキュメント、マーケティング |
| SSR | はい(リクエストごと) | 低 | 新鮮で動的なコンテンツ |
| ISR/ハイブリッド | はい | 低 | 毎時/毎日変わるコンテンツ |
そして、新しいビルドで避けるべき戦略の1つが動的レンダリングです。クローラーのユーザーエージェントを検出し、ユーザーにはCSRを提供しながら、クローラーには事前レンダリングされたバージョンを提供します。Googleは現在これを*“a workaround and not a long-term solution”(回避策であり、長期的な解決策ではない)と呼び、“creates additional complexities and resource requirements”*(追加の複雑さとリソース要件を生み出す)と述べ、代わりにサーバーサイドレンダリング、静的レンダリング、またはハイドレーションを推奨しています。(Bingは2018年に動的レンダリングを推奨していましたが、そのガイダンスは古くなっています。2019年以降、BingbotはMicrosoft Edge / Chromiumを介してレンダリングするため、SSR/SSGがそこでも正しい選択です。)
ここで否定すべき迷信があります。SSRはランキング向上にはなりません。John Mueller氏が述べたように、“there are no SEO ranking bonuses for implementing it one way or another”(どちらの方法で実装してもSEOランキングのボーナスはありません)— 異なるレンダリング方法は*“just different ways of making the content indexable”*(コンテンツをインデックス可能にするための単なる異なる方法)にすぎません。SSRの価値は、信頼性の高いインデックス可能性(そして多くの場合、より速いFirst Contentful Paintによるより良いCore Web Vitals)であり、魔法のようなランキングのレバーではありません。
ハイドレーションは完全に一致する必要があります — それはSEOテクニックではなく、バグの境界です
SSRとSSGはどちらも、ブラウザにすでにコンテンツが含まれるHTMLを渡します。Reactはその後、クライアント側でそのマークアップにアタッチする必要がありますが、これはプレーンなクライアントレンダリングとは異なるAPIです:
createRootは、既存のマークアップを期待せずに、ゼロからDOMノードにReactをレンダリングします。CSRのみのアプリに使用します。hydrateRootは、react-dom/serverがすでに生成したHTMLにReactをアタッチし、クライアントの最初のレンダリングがサーバーが送信したものと同一の出力を生成することを期待します。SSR/SSGを使用している場合は、createRootではなくhydrateRootが必要です。サーバーレンダリングされたマークアップでcreateRootを呼び出すと、Reactはそれを破棄してゼロから再レンダリングし、SSR/SSGを設定して得たまさにそのSEOの利点を捨ててしまいます。
サーバーとクライアントの出力の不一致は、SEO修正を行うReactアプリでは現実的なリスクです。タイトル内の Date.now()、ロケール依存の形式、if (typeof window !== 'undefined') ブランチなどです。React自身のドキュメントは、そのときに何が起こるかについて率直です。開発中は不一致について警告しますが、“there are no guarantees that attribute differences will be patched up in case of mismatches”(不一致の場合に属性の違いが修正されるという保証はありません)。ガイダンスは、不一致をバグとして扱い、修正することです。警告を抑制してコンテンツの同等性を想定することではありません。SEOに特化して言えば、ページがブラウザで正しく見えるからといって、レンダリングされたコンテンツとメタデータがサーバーが送信したものと一致するとは想定しないでください。クリーンなコンソールを信頼するのではなく、サーバーのHTMLをハイドレーション後のDOMと直接差分比較してください(以下のテストセクションのView SourceとInspect Elementのチェックは、これを高速に行う方法です)。
React RouterとURL構造
React Routerは、サーバーへの往復なしでブラウザ内のナビゲーションを処理します。これは、正しく設定されていればSEOに問題ありません:
- History APIを使用し、ハッシュルーティングは使用しないでください。
BrowserRouterはpushStateを使用し、クリーンでクロール可能なURL(/products)を生成します。HashRouterは/#/productsを生成し、GoogleはハッシュベースのURLを確実に解決できません。それらを機能させていた古いAJAXクローリングスキームは非推奨です。History APIを使用してください。 - サーバーもそれらのURLを処理する必要があります。 History APIルーティングでは、すべての「ページ」にサーバーが応答できる実際のURLが必要です。これはSSRにとって重要であり、
/productsへの直接アクセスやリフレッシュが404にならないようにするために必要です。 <Link>は実際のアンカーをレンダリングします。 React Routerの<Link>コンポーネントは<a href>を出力し、これはクロール可能です。アンカーなしのonClickハンドラーに基づくナビゲーションはクロールできません。Googleは実際の<a href>リンクのみをフォローします。
メタデータの管理:react-helmet、react-helmet-async、React 19のネイティブタグ
React 18までは、Reactはルート変更時にネイティブにドキュメントの<head>を更新することはありませんでした。各ルートの<title>、メタディスクリプション、canonical、Open Graph / Twitterタグはすべてライブラリで設定する必要がありました。React 19でそれが変わりました: コンポーネントは<title>、<meta>、<link>タグを直接レンダリングでき、Reactがそれらを<head>に自動的に引き上げます。クライアント専用アプリ、ストリーミングSSR、Server Componentsのいずれでも機能します。React 19.2は2026年半ば時点での現在の安定版リリースであり、これは現在のReactバージョンのあらゆるアプリに適用されます。
つまり、正しい答えはReactのバージョンと実際に必要なものによって異なります:
- React 19、スタンドアロンアプリ、基本的なタグのみ必要な場合。 コンポーネント内で
<title>/<meta>/<link>を直接レンダリングします。ライブラリは不要です。 - React 19だが、
htmlAttributes/bodyAttributes、SSRのcontextシリアライゼーション、onChangeClientState、prioritizeSeoTags、またはtitleTemplateが必要な場合。 ネイティブの引き上げではこれらをカバーできません。react-helmet-asyncを使用してください。その公式ドキュメントもこれを直接指摘しています: これらの特定のニーズがなければ、React 19ではパッケージがまったく不要な場合があります。 - React 18以前、スタンドアロンアプリの場合。 ネイティブの引き上げはまだ存在しません。
react-helmet-asyncを使用してください。これは積極的にメンテナンスされており(メジャーバージョン3、実行時にReactバージョンを検出)、SSRをサポートしています。 - 元の
react-helmet。 どのReactバージョンでも使用しないでください。メンテナンスされておらず(2020年以降リリースなし)、React 18の並行レンダリング下で既知のバグがあります。 - Next.jsアプリの場合。 Reactのバージョンに関係なく、Next独自のMetadata API(App Routerの
metadataエクスポート/generateMetadata)を使用してください。Helmetを追加したり、ネイティブのReactタグ引き上げに依存したりしないでください。Next.jsアプリではフレームワークがドキュメントを所有します。
JavaScript SEO全般から引き継がれる信頼性のルールが1つあり、上記のどれを使用する場合でも適用されます: HTMLレベルのメタデータはJSで注入されたメタデータよりも優れています。クライアントサイドのJavaScriptで注入されたcanonicalタグは、サーバーレンダリングされたHTMLに存在するものよりもはるかに信頼性が低くなります。これもSSR/SSGを支持するもう一つの論拠です。
AIクローラーがこれを緊急にしている
2026年の複雑な点: GPTBot(OpenAI)、ClaudeBot(Anthropic)、PerplexityBot、その他のエージェントのレンダリング動作は、プロバイダーとバージョンに固有です。CSR Reactアプリは、最初のGooglebotフェッチが見るのと同じ空のシェルをすべてのクローラーに送信します。現在のプロバイダードキュメントでは、それを埋める共有レンダリングステップは確立されていません。生成エンジンがより大きな発見サーフェスになるにつれて、SSR/SSGはGoogleだけの関心事ではなくなります: 生のHTMLは、普遍的なクローラーの制限を想定せずにカバレッジを最大化します。(ヘッドレスCMSのトピックでは、このAIクローラーの現実についてより詳しく説明しています。)
Googleが実際に見ているものをテストする
ブラウザを信頼しないでください。DevToolsインスペクタはレンダリングされたDOM(JavaScript実行後)を表示します。これは、JSなしのクローラーが見ないものとまったく同じです。適切なツールを使用してください:
- ソースの表示と要素の検査。 ソースの表示は生のHTML(JSの前にクローラーが取得するもの)です。要素の検査はレンダリングされたDOMです。コンテンツが要素の検査にはあるがソースの表示にはない場合、それはJavaScript依存です。
- URL検査ツール(Search Console) — 最も権威のあるチェックです。ライブテストを実行し、レンダリングされたHTML、スクリーンショット、ページリソース/コンソールメッセージを確認して、Googleが実際にレンダリングしたものと読み込みに失敗したものを確認します。
- リッチリザルトテスト — サイトを検証せずにレンダリングされたHTMLをすばやくチェックします。
- DevToolsでJavaScriptを無効化して再読み込みします。JSを実行しないクローラーの高速シミュレーションです(そしてAIクローラーが見るものの適切なプロキシでもあります)。
- Search Consoleのカバレッジレポート — 「検出済み、現在インデックス未登録」はレンダリングキューのバックログを示す可能性があります。重複ページのクラスターは、レンダリングタイムアウトのボイラープレートトラップを示す可能性があります。
- JSレンダリングクローラー — Ahrefs Site AuditとScreaming Frog(JSレンダリングモード)はページを大規模にレンダリングするため、サイト全体で生のHTMLとレンダリング後のHTMLを比較できます。
Next.js と Remix(実践的な答え)
SEO が重要で、生の CSR React を使っているなら、サーバーでレンダリングするフレームワークへの移行が通常は正しい選択です。Next.js はこのために作られています — SSR と SSG が標準で、ISR、App Router、組み込みの Metadata API、自動コード分割、画像最適化を備えています。Remix はウェブ標準の代替で、fetch/Request/Response を基盤とし、SSR がデフォルトで、プログレッシブエンハンスメントに強いという特徴があります。Next.js は別途詳しく説明します — ここでは意図的に簡潔にしています。React SEO の要点はもっと狭い範囲です:フレームワークは、コンテンツをブラウザのみのレンダリングステップから初期 HTML に移すために存在します。
React は、レンダリングを後付けではなくアーキテクチャ上の決定として扱う場合、SEO に適しています。ランキングが必要なものには SSR または SSG を選び、リンクとルーティングを正直に保ち、ルートごとにメタデータを管理し、ブラウザではなく Google 自身のツールに、実際にレンダリングされたものを確認させましょう。
AIまとめ
Advancedバージョンの簡潔な見解:
- React が SEO に悪いわけではない — デフォルトの CSR が悪いのです。 React ライブラリはクライアント、サーバー、静的、ストリーミングレンダリングをサポートしています。問題は、デフォルトの CRA/Vite ツールチェーン(サーバーなし)が空の
<div id="root">シェルを配信し、ブラウザで DOM を構築するため、クローラーが取得する生の HTML にコンテンツがないことです。 - Google は React をレンダリングできます(Web Rendering Service、常緑の Chromium を使用)が、レンダリングは別途キューに入れられ、遅延する可能性があり、ステートレスです(ロード間で cookies/localStorage/sessionStorage はありません)。
- レンダリングタイムアウトの罠: メインコンテンツの読み込みが遅いと、レンダリングがタイムアウトし、Google はボイラープレートのみのページをインデックスします。多くの URL にわたってそれらは同一に見え、重複としてフラグが付けられます(Gary Illyes の文書化された障害モード)。修正:コンテンツを最初に読み込む — または、より良いのは、レンダリングステップに依存しないことです(SSR/SSG)。
- レンダリング戦略、SEO に良い順: SSG(最もリスクが低い)≈ SSR ≈ プリレンダリング > ISR/ハイブリッド > CSR(最もリスクが高い)。動的レンダリングは非推奨です — Google は SSR、静的レンダリング、またはハイドレーションを推奨しています。
- SSR にランキングボーナスはありません — Mueller:「実装方法に関係なく SEO ランキングボーナスはありません。」コンテンツを確実にインデックス可能にするだけです。
- ハイドレーションはバグの境界であり、テクニックではありません: SSR/SSG アプリは
hydrateRoot(createRootではなく)でハイドレーションし、クライアントの最初のレンダリングがサーバーのものと正確に一致することを期待します。React は開発中に不一致を警告しますが、それらを修正することを保証しません — 不一致をバグとして扱い、サーバーとハイドレーション後の DOM を直接検証してください。 - React Router: ハッシュルーティングではなく History API(
BrowserRouter)を使用してください。サーバーはそれらの URL を処理する必要があります。<Link>はクロール可能な<a href>をレンダリングします —onClickのみのナビゲーションはレンダリングしません。 - メタデータ: React 19 は、基本のみが必要なスタンドアロンアプリのために
<title>/<meta>/<link>を<head>にネイティブにホイストします。React 18 の場合、または高度なニーズ(SSR コンテキスト、titleTemplate)がある場合は、react-helmet-async を使用してください — 2020 年以降メンテナンスされていない元の react-helmet は絶対に使用しないでください。Next.js では、React のバージョンに関係なく Metadata API を使用してください。HTML レベルが JS インジェクションより優れています。 - AI クローラーのレンダリングはプロバイダー固有です — CSR React は、各クローラーがサポートするかどうかわからないクライアント実行に依存します。SSR/SSG はコンテンツを初期 HTML に配置し、カバレッジを最大化します。
- テスト方法: ソースの表示とインスペクト、URL Inspection(レンダリングされた HTML + スクリーンショット + コンソール)、リッチリザルトテスト、JS 無効リロード、JS レンダリングクローラー。
- Next.js / Remix が実用的な修正です — コンテンツを初期 HTML に移します。
公式ドキュメント
検索エンジンからの一次情報ドキュメント。
- JavaScript SEOの基礎を理解する — クロール→レンダリング→インデックスのパイプライン、SPA、History API、JavaScriptによる正規化URL、意味のあるHTTPステータスコード。
- 検索関連のJavaScriptの問題を修正する — SPAでのソフト404処理、ステートレスレンダラー(CookieやlocalStorageなし)、URL Inspectionでのテスト。
- 動的レンダリング(非推奨の回避策) — Googleがこれを非推奨にした理由と、代わりに何を使うべきか(SSR、静的レンダリング、ハイドレーション)。
- 新しいJavaScript SEOビデオシリーズの紹介 — Martin Splittのシリーズで、React、Angular、Vueを具体的に取り上げています。
- Google検索の仕組みの詳細ガイド — クロール→インデックス→配信の中でレンダリングがどこに位置するか。
Bing / Microsoft
- 新しいエバーグリーンBingbot(Microsoft Edge) — Googlebotと同じChromiumプラットフォームでJavaScriptをレンダリングするBingbot。
- bingbotシリーズ:JavaScript、動的レンダリング、クローキング — Bingの古い(2018年)見解。歴史的な動的レンダリングの推奨に役立ちます。
技術リファレンス
- React v19(リリースノート) — コンポーネント内での
<title>、<meta>、<link>タグのレンダリングをネイティブサポートし、自動的に<head>にホイストします。 - createRoot / hydrateRoot(Reactドキュメント) — クライアントレンダリングとハイドレーションのAPIの分割、および不一致が必ずしも修正されるとは限らないという注意点。
- react-helmet-async(npm) — スタンドアロンのReactアプリで
<head>を管理するためのメンテナンス済みフォーク。React 18または高度なReact 19のニーズに対応。
ソースからの引用
Googleの検索チームによる公式声明。各検索エンジンのディープリンクは、ソースページの引用箇所にジャンプします。以下のスタッフの引用は、それを再掲した記事にリンクされています。
Googleドキュメント — SPAと動的レンダリング
- “Single-page applications (SPA) are websites that load an HTML document once and fetch any additional content using JavaScript APIs.” (翻訳) 「シングルページアプリケーション(SPA)は、HTMLドキュメントを一度読み込み、追加のコンテンツをJavaScript APIを使用して取得するウェブサイトです。」 — Google Search Centralドキュメント。 引用にジャンプ
- “Dynamic rendering was a workaround and not a long-term solution for problems with JavaScript-generated content in search engines.” (翻訳) 「動的レンダリングは回避策であり、検索エンジンにおけるJavaScript生成コンテンツの問題に対する長期的な解決策ではありませんでした。」 — Google Search Centralドキュメント。 引用にジャンプ
Martin Splitt(Google) — JavaScriptページがどのようにインデックスされるか
- “What we do is we do an HTTP request, and we get something back, right — some HTML, maybe it’s a barebone HTML and all it does is load the JavaScript and run the JavaScript. Then, this HTML that we got from the original HTTP GET request from the crawl, goes into rendering. Rendering runs JavaScript — boom!, a lot of content happens that wasn’t there before.” (翻訳) 「私たちがやっているのは、HTTPリクエストを送り、何かを受け取る——HTML、おそらく骨組みだけのHTMLで、JavaScriptを読み込んで実行するだけのものだ。そして、クロール時の最初のHTTP GETリクエストで得たこのHTMLがレンダリングに入る。レンダリングはJavaScriptを実行する——ドーン!そこには以前はなかった大量のコンテンツが生まれる。」 報道を読む (Search Engine Journal)
- 2つの波のモデルが不正確であることについて: “there’s no such thing as the second wave of crawling-ish. The wave is an oversimplification.” (翻訳) 「クロールのような第二の波などというものは存在しない。波というのは過度な単純化だ。」 報道を読む (Search Engine Roundtable)
Gary Illyes(Google) — レンダリングタイムアウトによる重複コンテンツの罠
- “I have a bunch of emails in my inbox where the issue is that the centerpiece took forever to load, so rendering timed out (my most likely explanation) and we were left with a bunch of pages that only had the boilerplate. With only the boilerplate, those pages are dups.” (翻訳) 「受信トレイには、中心となるコンテンツの読み込みに時間がかかりすぎてレンダリングがタイムアウトした(最も可能性の高い説明)という問題のメールがたくさんある。その結果、定型文しかないページが大量に残ってしまった。定型文しかない場合、それらのページは重複とみなされる。」
- “Do you have a JavaScript-heavy site and you see lots of dups reported in Search Console? Try to restructure the js calls such that the content (including marginal boilerplate) loads first and see if that helps.” (翻訳) 「JavaScriptを多用したサイトで、Search Consoleに多数の重複が報告されているのを見たことはあるか?コンテンツ(わずかな定型文を含む)が最初に読み込まれるようにJS呼び出しを再構成してみて、効果があるか確認してみてほしい。」 投稿を読む (LinkedIn)
John Mueller(Google) — レンダリング方法の選択によるランキングボーナスはなし
- “There are no SEO ranking bonuses for implementing it one way or another.” They’re “just different ways of making the content indexable (as is client side rendering).” (翻訳) 「どちらの方法で実装してもSEOのランキングボーナスはありません。」それらは「(クライアントサイドレンダリングと同様に)コンテンツをインデックス可能にするための単なる異なる方法」にすぎません。 報道を読む (Search Engine Roundtable)
React SEOチェックリスト
クローラーがReactアプリを認識してインデックスできることを確認するためのチェック:
- 重要なコンテンツがビューソース(生のHTML)に表示されること(レンダリングされたDOMだけでなく)— 欠けている場合は、CSRに依存していることになります。
- ランキングが必要なページはSSRまたはSSG(Next.js、Remix、またはプリレンダリングステップ)を使用し、 生のクライアントサイドレンダリングは使用しないこと。
- メインコンテンツが高速かつ最初に読み込まれること — レンダリングタイムアウトを引き起こして ボイラープレートのみのページになるような遅いAPIウォーターフォールがないこと。
- ルーティングはHistory API(
BrowserRouter)を使用し、HashRouter/#/URLは使用しないこと。 - サーバーがすべてのクライアントサイドルートに応答できること(直接アクセスやリフレッシュ時に404にならない)。
- ナビゲーションは実際の
<a href>リンク(React Router<Link>)を使用し、<div>/<button>上のonClickのみの ハンドラは使用しないこと。 - 各ルートが一意の
<title>、メタディスクリプション、canonical、OGタグを設定し、 ナビゲーション時に更新されること。 - メタデータがバージョンと一致すること:React 19のネイティブ
<title>/<meta>/<link>タグ(基本用)、react-helmet-async(React 18または高度なニーズ用、非推奨のreact-helmetは不可)、 またはNext.jsの場合はNext.js Metadata API。 - サーバーレンダリングの場合、
hydrateRoot(createRootではない)でハイドレーションし、 開発モードのハイドレーションミスマッチ警告は修正すべきバグとして扱うこと。 - クライアントサイドの「見つかりません」ルートは、
200ステータスのソフト404ではなく、 実際の404(またはnoindex)を返すこと。 - JavaScriptとCSSが
robots.txtでブロックされていないこと(Googleはブロックされたファイルからレンダリングしません)。 - 重要なコンテンツがcookies / localStorage / sessionStorageに依存していないこと(レンダラーは ステートレスです)。
- URL Inspectionで検証済み:レンダリングされたHTMLとスクリーンショットに実際のコンテンツが表示されること。
- カバレッジレポートで**「検出済み、現在インデックス未登録」**(レンダリングバックログ)と 重複クラスター(レンダリングタイムアウトの罠)を確認済みであること。
メンタルモデル
1. 唯一重要な質問:生のHTMLに何があるか? JavaScriptが実行される前のビューソースは、最初のフェッチクローラー — そしてほとんどのAIクローラーが、 永遠に — 見るものです。コンテンツがそこになければ、ブラウザでの見た目がどれだけ良くても、React SEOの問題があります。
2. レンダリングは別の、失敗しうるステップです。 クロール → レンダリング → インデックス。CSRはコンテンツの100%をレンダリングステップの向こう側に置きます。 レンダリングステップはキューに入れられ、遅延し、ステートレスで、タイムアウトする可能性があります。SSR/SSGはコンテンツをそのステップの 前に移動します。「2つの波」モデルを過信しないでください — Splitt自身もそれを 単純化しすぎと呼んでいました。
3. ボイラープレート重複の失敗モード。 遅いコンテンツ + レンダリングタイムアウト = すべてのURLがヘッダー/ナビ/フッターのみとしてレンダリングされる = Googleが 重複と見なす。修正は構造的です:コンテンツを最初に読み込むか、レンダリングステップへの依存をやめること。
4. レンダリングの決定ツリー。
- ランキングが必要またはAIに引用される必要がある公開コンテンツ → SSG(静的)またはSSR(新鮮)。
- ほぼ静的(ブログ、ドキュメント、マーケティング) → SSG、またはタイマーでのISR。
- 頻繁に変更され、新鮮である必要がある → SSR。
- ログイン済みダッシュボードで、インデックスされることを意図していない → CSRで問題ありません。
- SEOが必要な新しいビルド → 動的レンダリングではなく、Next.js / Remixを選択。
5. すべてのSEOシグナルに対してHTMLファースト、JSセカンド。 コンテンツ、リンク、canonical、タイトル、構造化データ — これらをサーバーレンダリングされたHTMLに含めます。 JSインジェクションされたSEOシグナル(JS canonicalタグやCSRでのreact-helmetを含む)は フォールバックとして扱い、計画としては扱わないでください:Googleはそれらを遅く見て、AIクローラーはまったく見ません。
React SEO — チートシート
レンダリングモードの概要
| モード | 初期HTMLにコンテンツがあるか? | SEOリスク | 使用用途 |
|---|---|---|---|
| CSR(生のReact) | いいえ | 最高 | ログイン済みダッシュボード、インデックス対象外アプリ |
| プリレンダリング(react-snap) | はい(ビルド時) | 低 | 小規模でほぼ静的なサイト |
| SSG | はい(ビルド時) | 最低 | ブログ、ドキュメント、マーケティング |
| SSR | はい(リクエストごと) | 低 | 新鮮で動的なコンテンツ |
| ISR / ハイブリッド(Next.js) | はい | 低 | 毎時/毎日のコンテンツ |
| 動的レンダリング | ボットのみ | 非推奨 | 使用しないでください — SSR/SSG/ハイドレーションを使用 |
一般的なReact SEOの間違い → 修正
| 誤り | 修正 |
|---|---|
| レンダリングされたDOMにのみコンテンツがある(CSR) | SSR / SSG / プリレンダリング |
ハッシュURL(/#/path) | History API(BrowserRouter) |
onClickナビゲーション、アンカーなし | 実際の<a href> / React Router <Link> |
| ルートでメタタグが更新されない | React 19: ネイティブの<title>/<meta>/<link>。古い/高度な場合: react-helmet-async。Next.js: Metadata API |
react-helmet(オリジナル、任意のReactバージョン) | react-helmet-async または React 19 のネイティブタグに切り替える |
サーバーレンダリングされたHTMLでcreateRootを使用 | 代わりに**hydrateRoot**を使用 — createRootはサーバーのマークアップを破棄する |
| ハイドレーション不一致の警告を抑制 | バグとして扱い、サーバー/クライアントの差異を修正する |
| 遅いコンテンツ → レンダリングタイムアウト → 重複 | メインコンテンツを最初に読み込む; SSR/SSGに移行する |
クライアント側の404が200を返す | 実際の404ステータスまたはnoindex |
robots.txtで.js / .cssをブロック | 許可する — Googleはブロックされたファイルをレンダリングしない |
| クッキー/localStorageで制限されたコンテンツ | しない — レンダラーはステートレスである |
クイックルール
- レンダラーはエバーグリーンChromiumで、キューされ、ステートレスで、タイムアウトする。
- SSRにランキングボーナスはない — 信頼できるインデックス可能性が重要(Mueller)。
- AIクローラーのレンダリングはプロバイダーによって異なる → 生のHTMLが最も安全なカバレッジのベースライン。
- Next.jsでは、Metadata APIを使用 — React Helmetではない。
- BingはJSをレンダリングする(Edge経由)が、信頼性は低い; そこでもSSR/SSGが安全な選択。
Reactが実行される前にサーバーが送信するものを確認する
代表的なインデックス可能なルートをurls.txtに入れる。これにより、生のレスポンスでCSRシェルと欠落したサーバーレンダリングされたヘッドマークアップが見つかる:
while IFS= read -r url; do
html=$(mktemp)
status=$(curl -sS -o "$html" -w '%{http_code}' "$url")
bytes=$(wc -c < "$html" | tr -d ' ')
title_count=$(grep -Eio '<title>[^<]*</title>' "$html" | wc -l | tr -d ' ')
canonical_count=$(grep -Eio '<link[^>]+rel=["'"']canonical["'"'][^>]*>' "$html" | wc -l | tr -d ' ')
printf '%s\t%s\tbytes=%s\ttitles=%s\tcanonicals=%s\n' "$status" "$url" "$bytes" "$title_count" "$canonical_count"
rm -f "$html"
done < urls.txtバイトサイズが小さいことは、それ自体がエラーではなく、レビューシグナルに過ぎない。フラグが付けられた生のレスポンスをレンダリングされたHTMLと比較し、主要なテキストとクロール可能なリンクが存在することを確認する。
React SEO監査のためのツール
- View Source vs. Inspect Element — 最速の最初のチェック。View Sourceは生のHTML(クローラーがJSの前に取得するもの); Inspect ElementはレンダリングされたDOM。InspectにはあるがView Sourceにはないコンテンツは、JavaScriptに依存している。
- URL Inspection (Google Search Console) — 真実の情報源。ライブテストを実行し、レンダリングされたHTML、スクリーンショット、ページリソース(読み込まれたもの vs. ブロックされたもの)、およびコンソールメッセージを表示して、Googleが正確に何をレンダリングしたかを確認する。
- Rich Results Test — サイトを検証せずに、単一のURLに対する迅速なレンダリング済みHTMLと構造化データのチェック。
- Chrome DevTools — JavaScriptを無効にする(コマンドメニュー → “Disable JavaScript”)そしてリロードして、HTMLのみのフェッチャーが受け取るものを検査する。これはカバレッジチェックであり、特定のAIクローラーの現在のレンダリング動作の証明ではない。
- Search Consoleのカバレッジレポート — 「検出済み・未インデックス」(レンダリングバックログ)と重複クラスター(レンダリングタイムアウトの定型文トラップ)を監視する。
- JSレンダリングクローラー — Ahrefs Site Audit と Screaming Frog SEO Spider(JSレンダリングモード)はJavaScriptを実行するため、サイト全体で生のHTMLとレンダリングされたHTMLを比較できる。
Reactチームが実際に犯す間違い
仮説ではなく、出荷されるCSR Reactアプリで私が繰り返し見かける具体的なパターン。それぞれが予防策 — インデックス可能性を損なう前に捉える。
ランキングが必要なページに生のCRA/Vite CSRを出荷する
チームは、マーケティングページ、ブログ記事、製品ページ — 検索に表示される必要があるまさにそのコンテンツ — のために、Create React AppまたはVite + Reactを本番環境に直接出荷する。
なぜ間違いか: サーバーはほぼ空の<div id="root">シェルを送信する; 実際のコンテンツはJavaScriptが実行された後にのみ存在するため、Googleのレンダリングキューによって遅延され、クライアント実行なしで初期HTMLを取得するAIクローラーには見えない可能性がある。
代わりにすべきこと: ランキングや引用が必要なものはすべてSSRまたはSSGに移行する — Next.jsまたはRemixが最も簡単なパス — そして、ダッシュボードのようなログイン済みでインデックスされないサーフェスには生のCSRを予約する。
ハッシュURLでのルーティング(HashRouter)
React RouterのHashRouterを使うのは、最も抵抗が少ない道だからです。サーバー設定が不要で、どんな静的ホストでも動作します。なぜ間違いなのか: Googleは/#/products形式のURLを確実に解決できません。ハッシュフラグメントをクロール可能にしていた古いAJAXクローリング方式は非推奨です。代わりにすべきこと: BrowserRouter(History API)を使用し、サーバーが生成するすべてのルート(ディープリンクへの直接アクセスやリフレッシュを含む)に応答するようにしてください。
実際のアンカーではなくonClickでナビゲーションを構築する
<div>や<button>にonClickハンドラーを設定してナビゲーションを配線する。多くの場合、スタイルを簡単にしたり、デフォルトのリンク動作を避けたりするためです。なぜ間違いなのか: Googleは実際の<a href>リンクのみを追跡します。クリックハンドラーを持つ<div>は、マウスに対してどう動作するかに関係なく、クロールでは見えません。代わりにすべきこと: React Routerの<Link>コンポーネント(内部で実際の<a href>をレンダリングする)を使用するか、外部ナビゲーションにはプレーンなアンカータグを使用してください。
遅いAPIウォーターフォールの背後にメインコンテンツを読み込む
ヘッダーとナビを高速に取得し、その後、実際のページコンテンツ(各URLをユニークにする部分)が表示される前に、複数のAPI呼び出しを連鎖させます。なぜ間違いなのか: GoogleのWebレンダリングサービスはタイムアウトを強制します。中心となるコンテンツの読み込みが遅い場合、レンダリングはそれが到着する前に終了し、Googleはボイラープレートのみのページをインデックスし、それらが互いの重複としてフラグ付けされることになります。これはGary Illyesが説明したまさにその障害モードです。代わりにすべきこと: メインコンテンツが最初に読み込まれるようにリクエストを再構成するか、SSR/SSGでクライアントサイドレンダリングへの依存を完全に取り除いてください。
元のreact-helmetをまだ使っている
ルートごとの<title>やメタタグにreact-helmetを使うのは、古いチュートリアルがすべて推奨しているライブラリだからです。なぜ間違いなのか: 元のパッケージはメンテナンスされておらず、2020年以降リリースがなく、React 18の並行レンダリングとSSRで既知の問題があります。代わりにすべきこと: React 19では、コンポーネント内で<title>/<meta>/<link>を直接レンダリングし、Reactにホイストさせます(基本にはライブラリは不要)。React 18、またはSSRコンテキストのシリアライゼーションやtitleTemplateなどの高度なニーズには、メンテナンスされたフォークであるreact-helmet-asyncを使用してください。Next.jsでは、組み込みのMetadata APIを使用し、その上にHelmetを追加しないでください。
robots.txtでJavaScriptやCSSをブロックする
robots.txtで/static/js/やバンドラーのアセットフォルダーをブロックする。古いクロール予算の懸念から残っていたり、別のサイトの設定からコピーされたりすることがあります。なぜ間違いなのか: Googleは取得を許可されていないものをレンダリングできません。バンドルがブロックされると、ソースコードが正常でも、Webレンダリングサービスは不完全(または空の)DOMを構築します。代わりにすべきこと: クローラーがJSとCSSを取得できるようにし、URL検査ツールのページリソースチェックで重要なものがブロックされていないことを確認してください。
自分でテストする: React SEO
Reactアプリをクロール可能かつインデックス可能にするための5つの簡単な質問。それぞれ答えを選んで、確認してください。
時間をかける価値のあるリソース
関連する私の記事
- React SEO: Best Practices to Make It SEO-Friendly (Ahrefs) — AhrefsのReact SEOガイド。私がレビューしたもので、この記事はより深く、ソースリンク付きの扱いです。
- JavaScript SEO: A Definitive Guide (Ahrefs) — 基礎となるレンダリングメカニズムに関する私の完全ガイド: DOMパリティ、最も制限的なディレクティブのルール、正規化とメタタグの処理、レンダリングの選択。React固有の修正の背後にある一般的なケースについてはこれを読んでください。
- The Beginner’s Guide to Technical SEO (Ahrefs) — React/JavaScript SEOが全体像の中でどこに位置するか。
私の講演
- 検索の仕組み(SlideShare)— クロール、レンダリング、インデックス、ランキングの解説。 (私の常時の免責事項が適用されます:「これは私のシステムの理解に基づくもので…100%完全または正確ではありません。」)
業界からの情報
- JavaScript SEOの基礎を理解する(Google)— SPA、History API、正規化/ステータスコード処理に関する一次情報のドキュメント。
- 動的レンダリング(非推奨)(Google)— 動的レンダリングが長期的な解決策ではなく回避策である理由。
- Martin SplittがJavaScriptサイトのインデックス方法を解説(Search Engine Journal)— クロール→レンダリング→インデックスの流れを「barebone HTML … boom」と説明した内容。
- Gary Illyes氏、JS多用サイトと重複コンテンツについて(LinkedIn)— レンダリングタイムアウトから重複コンテンツに至る障害モードを本人の言葉で説明。
- クライアントサイドReactアプリの技術的SEO問題を修正する方法(Search Engine Land)— CSR Reactアプリの監査と修正に関する実例のケーススタディ。
- SSRと動的レンダリング—ランキングに差はない(Search Engine Roundtable)— Mueller氏の「SEOランキングボーナスはない」という発言。
- react-helmet-async(npm)— スタンドアロンReact向けにメンテナンスされているhead管理ライブラリ。
- 新しいエバーグリーンBingbot(Microsoft Edge)(Bing)— Googlebotと同様にChromiumを介してJSをレンダリングするBingbot。
動画
変更履歴
2026年7月18日に更新。
編集概要と記録された変更の詳細。変更の詳細
-
変更の詳細な注記は現在英語でのみ提供されています。
-
変更の詳細な注記は現在英語でのみ提供されています。
-
変更の詳細な注記は現在英語でのみ提供されています。
完全な比較は利用できません — この改訂の以前のスナップショットがアーカイブされていません。