JavaScript SEO とレンダリング
Google が JavaScript をどうレンダリングするか、そして SEO のために SSG / SSR / ISR / 動的レンダリングをどう選ぶか。
最近のフロントエンドフレームワークは、コンテンツの大半をブラウザ上で実行される JavaScript として配信する。これはプロダクトエンジニアリングには素晴らしいが、「クローラーが HTML をダウンロードして読む」という SEO の素朴なメンタルモデルにとっては最悪だ。Google は JavaScript を実行する。ただし、独自のタイムラインで、独自のバジェットの範囲内で、しかも順位をひそかに損なう失敗パターンを伴って実行する。最も重要なコンテンツが fetch() の解決後にしか存在しないなら、あなたはオーガニックトラフィックを「数日遅れるかもしれない、あるいは期待どおりに完了しないかもしれないレンダリング」に賭けていることになる。
このガイドでは、Google のレンダリングパイプラインの内部で実際に何が起きているのかを説明し、ビルド時に選べるレンダリング戦略を比較し、実際のアプリを痛い目に遭わせる落とし穴を一つずつ見ていき、今日から実行できるデバッグの手順を示す。
Google のレンダリングの仕組み
インデックス登録は単一のパスではなく、2 つの波からなるプロセスだと考えると理解しやすい。
- クロール。 Googlebot が URL をリクエストし、初期 HTML を受け取る。これは
curl https://example.comが返すものとまったく同じだ。ここで発見されたリンクはクロールのフロンティアに入る。初期 HTML が空の<div id="root"></div>だけなら、この波はほとんど何も見えない。 - レンダリング。 URL は Web Rendering Service(WRS) のキューに入れられる。WRS はヘッドレスで常に最新の Chromium だ。キャパシティに余裕ができると、ページを読み込み、JavaScript を実行し、ネットワークが落ち着くのを待ち、レンダリング後の HTML(JavaScript 実行後の DOM)を生成する。
- インデックス。 Google はレンダリング後の HTML をインデックスに登録し、そこで見つかった新しいリンクを次のクロールサイクル向けに抽出する。
決定的に重要な洞察は、波 1 と波 2 のあいだのギャップだ。JavaScript の実行は HTML の取得よりおよそ一桁コストが高いため、レンダリングは後回しにされる。実際には遅延が数分のことも多いが、クロール需要が高いときやサイトのレンダリングキューが大きいときには数日に伸びることもある。
CRAWL RENDER (WRS queue) INDEX
Googlebot ──► raw HTML ──► [ wait: minutes → days ] ──► headless
│ Chromium
▼ │
links found rendered DOM
in raw HTML │
└──────────────► next crawl cycle ◄── new links found
この後回しは、あなたが直接コントロールできない 2 つのバジェットと相互作用する。
- クロールバジェット — ある期間内に Googlebot があなたのオリジンから取得する URL の数。サイトの権威性、サーバー速度、エラー率によって左右される。
- レンダリングバジェット — ざっくり言えば、WRS があなたのページのレンダリングに費やしてよい CPU/時間の量。遅くてスクリプトが重いページはこれを多く消費するため、サイクルごとにレンダリングされるページが少なくなる。
⚠️ 注意: 典型的な失敗は、クライアントサイドでの挿入後にしか存在しないコンテンツだ。クロールの波は空の殻しか見えず、レンダリングの波が遅延したり、スクリプトが WRS 上でエラーになったりすると、そのコンテンツは決してインデックスに登録されない。「自分のブラウザでは動く」は、Googlebot にとっても動くことの証拠にはならない。あなたのブラウザはレート制限もサンドボックス化もされておらず、数週間前のキャッシュ済みレスポンスで動いているわけでもない。
開発者を驚かせる WRS の細かな仕様をいくつか挙げる。
- WRS はスクロールもクリックもせず、マウスも動かさない。ユーザー操作の背後にあるものはすべて WRS から見えない。
- WRS は権限リクエスト(位置情報、カメラ、通知)を自動的に拒否する。これらのプロンプトを待つコードはハングする。
- WRS は独自のスケジュールでリソース(JS、CSS、API レスポンス)を積極的にキャッシュする。そのスケジュールはあなたの HTTP キャッシュヘッダーが示すよりずっと長いことがある。「新鮮なデプロイ」が古いアセットに対してレンダリングされることもある。
- WRS は最新の Chromium を使うため、モダンな JS 構文は問題ない。だが、レンダリングをまたいで頼れる
localStorage/sessionStorageの永続性はなく、Cookie もログイン済みユーザーのようには持ち越されない。
レンダリング戦略
HTML をどこで生成するかによって、このリスクをどれだけ背負うかが決まる。実際に選ぶことになる 5 つの戦略を以下に示す。
| 戦略 | HTML が生成されるタイミング | クローラーがすぐにコンテンツを見られる? | SEO との相性 | 向いている用途 |
|---|---|---|---|---|
| CSR(クライアントサイドレンダリング) | ブラウザ上、JS 実行後 | いいえ — 最初は空の殻 | ⚠️ リスクあり | ログイン後のダッシュボード、認証背後のアプリ的 UI |
| SSR(サーバーサイドレンダリング) | リクエストごとにサーバー上で | はい | ✅ 強い | パーソナライズされた、または頻繁に変わる公開ページ |
| SSG(静的サイト生成) | ビルド時 | はい | ✅✅ 最強 | ドキュメント、ブログ、マーケティング、安定したもの全般 |
| ISR(インクリメンタル静的再生成) | ビルド時に生成し、その後スケジュール/オンデマンドで再生成 | はい(直近の正常な静的版を配信) | ✅✅ 強い | 変化はするがリクエストごとではない大規模カタログ |
| 動的レンダリング | リクエストごと。ボットには事前レンダリング済み HTML、ユーザーには CSR | はい(ボットに対して) | ⚠️ 回避策 | 書き換えられないレガシー SPA |
表に収まりきらない補足をいくつか。
- CSR は素の
create-react-appや未設定の SPA のデフォルトだ。本ガイドで挙げる JavaScript-SEO の問題のほぼすべてを生み出す戦略でもある。インデックスすべきものが何もないログイン背後なら、まったく問題ない。 - SSR は最初のリクエストで完全な HTML を返し、それを「ハイドレート」してインタラクティブなアプリにする。クローラーは波 1 で本物のコンテンツを受け取るので、レンダリングの後回しが問題にならなくなる。コストはリクエストごとのサーバー計算とレイテンシだ。
- SSG はビルド時にすべてのページを静的な
.htmlファイルに事前レンダリングする。待つべきレンダリングも、遅くなりうるサーバーも存在しない。クローラーは CDN が配信するのと同じファイルを読む。これはコンテンツサイトのゴールドスタンダードだ。 - ISR は更新できる SSG だ。ページは静的だが、N 秒後やオンデマンドでバックグラウンド再生成できる(例: Next.js の
revalidate)。クローラーは常に有効な静的ページを受け取り、鮮度はわずかに遅れる。5 万件の商品ページがあり、価格が変わるたびに全部を再ビルドするのが現実的でないときに最適だ。 - 動的レンダリング はユーザーエージェントを嗅ぎ分け、ボットには(Prerender.io や Puppeteer のようなもので)事前レンダリングしたスナップショットを、本物のユーザーには SPA を配信する。Google は現在これを推奨ではなく回避策と呼んでいる。壊れやすい UA 検出レイヤーと、保守すべき 2 つ目のレンダリングパイプラインが増えるからだ。アプリの書き換えが選択肢にないときだけ手を伸ばすこと。
🧑💻 開発者視点: これは誰がレンダリングコストを払うかの問題だと考えるとよい。CSR はユーザーの端末と Google の WRS に(二重に)払わせる。SSR は毎リクエスト、自分のサーバーに払わせる。SSG はビルド時に一度払って二度と払わない。そのコストを左へ — つまりビルド時へ — 押しやるほど、Google のレンダリングバジェットの消費は減り、レンダリングが失敗しうる経路も減る。
落とし穴
これらは実際の監査で現れる問題を、サイレントなトラフィック損失を引き起こす頻度の高い順におおまかに並べたものだ。
1. <head> を決して更新しないクライアントサイドルーティング。 SPA ではルート間の遷移が、フルページ読み込みなしに DOM を入れ替える。ルーターがタイトル、メタディスクリプション、canonical も更新しなければ、すべてのルートが初期 HTML にあったものを引き継ぐ。結果として Google は、すべて自分のホームページだと主張する 10 個のページをインデックスすることになる。
// ❌ Title and canonical are stale on every soft navigation
router.push('/pricing'); // DOM changes, <head> does not
// ✅ Update head metadata on every route change
router.afterEach((to) => {
document.title = to.meta.title;
document
.querySelector('link[rel="canonical"]')
.setAttribute('href', `https://example.com${to.path}`);
});
2. スクロールや操作を必要とする遅延読み込みコンテンツ。 無限スクロール、「もっと読み込む」ボタン、IntersectionObserver でマウントされるコンテンツは、WRS がスクロールもクリックもしないため WRS からは見えない。記事本文や商品リストがスクロールイベント後にしか現れないなら、それは Google がインデックスするレンダリング後の DOM には存在しない。
3. ハッシュベースのルーティング。 example.com/#/products/42 のような URL は古い SPA の遺物だ。# 以降はすべてフラグメントであり、仕様上サーバーには決して送られず、Google はこれを example.com/ と同じ URL として扱う。ハッシュルートはサイト全体を 1 つのインデックス可能ページに押し潰してしまう。常に History API(pushState)を使い、ルートを実在しサーバーで解決できるパスにすること。
4. タイムアウトとレンダリングをブロックする処理。 WRS は時間がかかりすぎるレンダリングを途中で放棄する。依存し合う fetch() のウォーターフォール、4 MB のバンドル、ハングするサードパーティスクリプトは、コンテンツを打ち切り時間の先へ押しやりうる。遅いレンダリングはレンダリングバジェットも食いつぶすので、そもそもレンダリングされるページが減る。
5. ユーザー操作や同意の背後にゲートされたコンテンツ。 「同意する」を押すまでレンダリングをブロックする Cookie 同意ウォール、クリック時にしか中身が読み込まれないタブ、「続きを読む」ボタンで現れる本文 — WRS はこれらをどれもクリックしない。SEO 上重要なら、操作なしで DOM に存在していなければならない。
6. ソフト 404 と JS 駆動のエラー状態。 SPA はリソースが見つからないとき、しばしば「Not found」メッセージを表示しつつ HTTP 200 を返す。Google は中身の薄い成功レスポンスを見て、エラーページをインデックスしてしまうことがある。サーバーから本物の 404/410 ステータスを返すか、本当に存在しないコンテンツには noindex を使うこと。
ベストプラクティス
一貫した方針はこうだ。重要なコンテンツとメタデータを、JavaScript の実行前に存在する HTML に入れ、JavaScript は拡張として扱う。
- 重要なコンテンツはサーバーサイドかビルド時にレンダリングする。 見出し、本文、主要ナビ、商品詳細、価格は、SSR か SSG を通じて初期 HTML に含めるべきだ。JavaScript にはページを作らせるのではなく、すでに完成したページを拡張させること。
- プログレッシブエンハンスメントを実践する。 JS を無効にしてもページは中核的な意味を伝えられるべきだ。リンクは実在の
<a href>要素であるべきだ(WRS は<a href>をたどるが、router.pushを呼ぶonClickハンドラはたどらない)。遷移するボタンはリンクにすること。 - History API を使い、ルートごとにメタデータを更新する。 すべてのルートには固有の
title、メタディスクリプション、自己参照のcanonicalが必要で、遷移時に更新する。フレームワークの head マネージャ(react-helmet-async、@unhead/vue、Next/Astro の組み込み機能)がこれをきれいに処理してくれる。
// Per-route head with a framework-agnostic shape
function setSeoHead({ title, description, canonical }) {
document.title = title;
upsertMeta('description', description);
upsertLink('canonical', canonical);
}
- 構造化データは遅延 fetch の背後ではなく HTML に入れる。 JSON-LD はできるだけ早くレンダリング後の DOM に存在すべきだ。SSG/SSR はこれを直接出力する。どうしてもクライアントサイドで注入するなら、マウント時に同期的に行い、非同期のデータ往復の後では決して行わないこと。
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "JavaScript SEO & Rendering",
"datePublished": "2026-06-22"
}
</script>
- JS を軽量で堅牢に保つ。 コード分割し、重要でないスクリプトを遅延させ、サードパーティ 1 件の失敗でページが真っ白にならないようにすること。アナリティクスやタグマネージャはセルフホストするか
asyncにして、コンテンツを決してブロックさせない。 robots.txtで JS/CSS をブロックしない。 WRS はページをレンダリングするためにスクリプトとスタイルを取得する必要がある。/_next/や/assets/をブロックすると、Google は壊れたページをレンダリングすることになる。
💡 ヒント: どんなページにも使える素早い健全性チェック —
curl -s https://your-url | grep "your headline text"を実行する。主要なコンテンツがその出力に現れないなら、クロールの波はそれを見られず、あなたは後回しのレンダリングの波に完全に頼っていることになる。これが SSG/SSR(コンテンツが存在する)と CSR(コンテンツが存在しない)を分ける一線だ。
デバッグ
Google が何を見ているかを推測する必要はない。Google のツールがレンダリング後の出力を直接見せてくれる。
- Google Search Console の URL 検査。 公開 URL を検査し、テスト済みのページを表示 → HTML を開く。これは WRS が生成したレンダリング後の HTML だ。見出し、canonical、JSON-LD をここで検索すること。ここに無いなら、インデックスにも無い。スクリーンショットタブと詳細情報 → ページのリソースタブは、レンダリングを壊したブロック済みスクリプトやコンソールエラーを明らかにする。
site:演算子。site:example.com/pricingやsite:example.com "ページ内の正確なフレーズ"で、あるページ(および JS で注入された特定のテキスト)が実際にインデックスされているかが分かる。レンダリング後の DOM にしか無いフレーズで結果が出ないなら、レンダリングが定着していない強い兆候だ。- 生の DOM とレンダリング後の DOM を自分で比較する。
view-source:(またはcurl)はクロールの波の HTML を見せる。DevTools → Elements は JS 実行後の DOM を見せる。両者の差分こそが、レンダリングの成功に依存するコンテンツそのものだ。SEO 上重要なものについては、この差分を最小化すること。 - Googlebot の制約をシミュレートする。 Chrome DevTools で JavaScript を無効化し(コマンドメニュー → “Disable JavaScript”)リロードする。これがクロールの波を近似する。ネットワークと CPU をスロットリングして、WRS の負荷下でタイムアウトしうるレンダリングをあぶり出すこと。無料のリッチリザルトテストとモバイルフレンドリーテストも Google 自身のエンジンでレンダリングし、レンダリングエラーを報告してくれる。
⚠️ 注意: 修正を検証するときは、Google のキャッシュ版ではなく常に公開の URL をテストすること。キャッシュは直近の成功したレンダリングを反映しており、それはあなたのデプロイより数日古いことがある。
ここから先へ
JavaScript レンダリングは本質的に、あなたがビルドレイヤーで下す決定だ。選ぶフレームワークと出力モードが、そもそもこれらの問題に直面するかどうかを決める。そのトレードオフはビルドレイヤーのガイドで掘り下げている。
このサイト自体は、Astro と静的生成を使うことで、この種の問題をまるごと回避している。すべてのページがビルド時に HTML へ事前レンダリングされるので、クロールの波は後回しのレンダリングキューにまったく頼ることなく、完全なコンテンツ、構造化データ、メタデータを見られる。これは偶然ではない。利用できる中で最も SEO に優しいデフォルトであり、コンテンツサイトがまず SSG に手を伸ばすべき理由だ。
重要なポイント
- ✅ Google は JavaScript を後回しの第 2 波(WRS)でレンダリングする。注入されたコンテンツがすぐに — あるいはそもそも — インデックスされると決して思い込まないこと。
- ✅ レンダリングコストはビルド時へ押しやる。SSG が最強、SSR が強い、CSR はインデックスさせたいものにはリスクがある。
- ✅ クライアントサイドのルート遷移ごとに
title、メタディスクリプション、自己参照のcanonicalを更新する。 - ✅ 重要なコンテンツと JSON-LD は JS 実行前に存在する HTML に入れる。WRS はスクロールもクリックもせず、権限も付与しない。
- ✅ GSC の URL 検査(レンダリング後の HTML)、
site:演算子、JS 無効化リロードで検証する。キャッシュではなく公開 URL をテストすること。