LocalBusiness構造化データ、JSで入れてもいいがサーバーサイドの方が確実な理由
店舗検索ページのLocalBusiness JSON-LDをJSで注入すると、生のHTMLにはブロックが0個。サーバーサイド出力と直接比較し、Google公式見解と順位の限界まで整理した。
店舗検索ページを作るとき、開発者が最も後回しにし、最も雑に扱うのが構造化データだ。画面に見える地図と営業時間さえ合っていれば終わった気がするからだ。ところがローカル検索(Googleマップ・ローカルパック)で自店がきちんと拾われるかは、目に見える画面ではなく、クローラーが読み取るマークアップで決まる。そしてそのマークアップをJavaScriptで後から描き込むか、サーバーが最初から出力するかが、思った以上に大きな差を生む。
まず誤解を一つ解いておこう。「JSで構造化データを入れるとGoogleが読めない」という話は間違いだ。
Google公式の立場:JS構造化データは対応されている
Googleは明示的に対応していると述べている。公式文書の表現をそのまま引くと、
“Google can read JSON-LD data when it is dynamically injected into the page’s contents, such as by JavaScript code or embedded widgets.” — Google Search Central, Intro to Structured Data
つまりdocument.createElement('script')でapplication/ld+jsonブロックを後から付けても、Googleはレンダリング後のDOMからそれを読む。ここまでなら「ならJSで入れてもいい」で正しい。問題は、いつ、どれだけ確実に読まれるかだ。
直接検証:生のHTMLに何が残るか
クローラーはページを二度見る。最初は生のHTMLをそのまま取得し(一次)、資源が許すときにヘッドレスChromiumでJSを実行して再度見る(二次)。だから一次時点の生のHTMLに構造化データが既にあるかが、実質的な差になる。
このブログを実験台に載せて仮説を数字で検証してきた方式のまま、今回はレンダリングを直接測ってみた。同じLocalBusinessマークアップを二方式で用意した。一方はサーバーが出力した静的HTML、もう一方はJSがランタイムで注入する方式だ。
<!-- (A) サーバーサイド:生のHTMLにそのまま存在 -->
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"LocalBusiness","name":"明るい目メガネ店 渋谷店"}
</script>
<!-- (B) JS注入:生のHTMLには無く、実行後にのみ生成 -->
<script>
const ld = document.createElement('script');
ld.type = 'application/ld+json';
ld.textContent = JSON.stringify({ "@type": "LocalBusiness", name: "明るい目メガネ店 渋谷店" });
document.head.appendChild(ld);
</script>
JSを実行していない生のHTMLから、実際の<script type="application/ld+json">ブロックだけを解析してみた。
[A] サーバーサイド:ld+jsonブロック 1個、@type=['LocalBusiness']
[B] JS注入: ld+jsonブロック 0個、@type=[]
サーバーサイドは一次クロールが取得する生のHTMLに既にブロックがある。JS注入方式は生のHTMLにブロックが0個だ。そのブロックはブラウザがJSを実行した後にしかDOMに現れない。クローラーからすれば、二次レンダリングを待って初めて存在するデータだ。
二つの方式がクローラーの目にどう違って映るかを流れで描くとこうなる。
graph TD
A["クローラーがページを要求"] --> B["1回目:生のHTMLを取得"]
B --> C{"生のHTMLに<br/>ld+jsonブロックはあるか"}
C -->|"(A) サーバーサイド出力"| D["構造化データを即座に認識"]
C -->|"(B) JS注入"| E["ブロック0個 — 読むものが無い"]
E --> F["レンダリングキューで待機<br/>(リソース依存)"]
F --> G["2回目:ヘッドレスレンダリング"]
G -->|"成功"| H["DOMからld+jsonを認識"]
G -->|"遅延・失敗"| I["その日のデータは無し"]
実測結果と特性を整理すると:
| 項目 | (A) サーバーサイド出力 | (B) JS注入 |
|---|---|---|
| 生のHTMLのld+jsonブロック | 1個(実測) | 0個(実測) |
| 1回目クロール時点の認識 | 即座 | 不可 |
| レンダリングキュー依存 | 無し | 有り(リソース依存) |
| 失敗モード | 実質無し | レンダリング遅延・JSエラーでデータ欠落 |
| 適したデータ | NAP・座標など正確性が信頼そのものの値 | サーバー出力が不可能なウィジェット・サードパーティ連携 |
なぜこの差がローカルで特に重要か
Googleも動的生成マークアップのリスクを認めている。EC文脈の案内だが原理は同じだ。
“dynamically-generated markup can make Shopping crawls less frequent and less reliable, which can be an issue for fast-changing content.” — Google Search Central, Generate Structured Data with JavaScript
レンダリングキューは常に資源に依存する。二次レンダリングが遅れたり、JS実行中に外部スクリプトの読み込みが失敗したりすると、その日そのページのJSON-LDはそもそも生成されない。店舗名・住所・電話(NAP)のように正確さと一貫性が信頼の根拠になるデータを、毎回レンダリング成功に賭ける理由はない。サーバーサイド出力にはその依存がない。
正直な限界 — 構造化データは順位を上げない
ここで必ず押さえておくことがある。構造化データをサーバーサイドで完璧に出力しても、それ自体で検索順位は上がらない。Googleは構造化データがリッチリザルト表示の資格を与えるだけで、表示や順位を保証しないと文書で明言している。ローカル順位の実際の原動力はGoogleビジネスプロフィール(GBP)の運用、口コミ、カテゴリの正確さの側だ。サイトのマークアップはその効果を補助するにとどまる。
もう一つ。構造化データは画面に見える内容を忠実に反映しなければならない。
“don’t add structured data about information that is not visible to the user, even if the information is accurate.” — Google Search Central
GBPに登録した住所とページのマークアップの住所が食い違えば、役立つどころか信頼を削るだけだ。
では、JS注入で十分なのはいつか
すべてをサーバーサイドに移せという話ではない。次の状況ならJS注入も実用的な選択だ。
- サードパーティのウィジェットがマークアップを作るとき:予約・レビューウィジェットが自らJSON-LDを注入する構造なら、それをサーバーへ移すコストが利益を上回りうる。
- サーバーテンプレートに触れないとき:レガシーCMSやタグマネージャー(GTM)しか触れない環境ならJS注入が唯一の選択肢だ。GoogleもGTM経由の注入をサポート対象として文書化している。
- 正確性が画面と一緒に動くとき:どうせクライアントで計算して画面に描く値なら、マークアップも同じコードパスで作るほうが画面とマークアップの不一致を減らせる。
逆に店舗名・住所・電話のように値がほとんど変わらず、正確性がそのまま信頼になるデータなら、サーバーサイドが既定値だ。判断基準は一つ — 「このデータは2回目のレンダリングまで待ってもよいか、そしてレンダリングが失敗した日にも存在すべきか」。
開発者が今日できること
- 店舗のNAPと座標、URLをサーバーサイド(ルート/テンプレート)で静的に出力する。JS注入に依存しない。
- その値をGBPと正確に一致させる。不一致・ダミー・未検証の値(偽のSNS URLなど)は入れない。
- 画面に見えない情報はマークアップしない。
- デプロイ後、リッチリザルトテストとURL検査ツールで生とレンダリング両方を確認する。Lighthouseのアクセシビリティスコアを55から100まで実測で引き上げた記録のように、「JSで入れたから大丈夫だろう」という推測ではなく計測で決着をつける。
- 順位効果を約束しない。マークアップはクローラーの理解を助ける補助だという線を守る。
同じ情報でも機械にどのフォーマットで渡すかがコストと認識を変えることをトークン単位で実測したことがあるが、構造化データのサーバーサイド・JSの選択も結局は同じ筋の問題だ。
まとめると、JS構造化データは間違った方法ではない。ただしローカル店舗情報のように確実性がそのまま信頼になるデータなら、レンダリングキューに運を委ねるより、サーバーが最初から出力する方が防御的で予測可能だ。
店舗検索ページの構造化データをサーバーサイドで確実に出力したい、あるいは既存サイトのローカルSEO・マークアップ構造を点検したいなら、個人的に相談・実装のご依頼を受けています。jangwook.netのプロフィールの連絡先から気軽にどうぞ。
よくある質問
JSで入れた構造化データはGoogleに読まれないのですか?
では必ずサーバーサイドが正解ですか?
構造化データを入れると検索順位は上がりますか?
デプロイ後は何で確認しますか?
他の言語で読む
- 🇰🇷 한국어
- 🇯🇵 日本語(現在のページ)
- 🇺🇸 English
- 🇨🇳 中文