先に要点
- 構造化データは、記事・著者・公開日などの意味を機械に伝える補助情報です。schema.orgは語彙、JSON-LDは記述形式で、役割が違います。
- 正しく書けても、検索結果への表示や順位上昇、AIによる引用は保証されません。本文と一致させることが先です。
- 実装では、公開画面と同じデータから生成し、JSONの構文・HTMLへの埋め込み・本文との一致を別々に確認します。
構造化データを入れたのに検索結果が変わらないときは、コードを増やす前に「形式が正しいか」「Googleが対応する表示機能か」「ページ内容と一致するか」を分けて確認します。これらは別の問題なので、テストツールの合格だけで表示されるとは限りません。
この記事は、技術ブログへ構造化データを実装する人向けに、型の選択、PHPでの生成、安全な埋め込み、公開後の確認までを扱います。コード例は2026年9月21日にPHP 8.5.1で実行検証しています。Google・Schema.org・PHPの公式資料も同日に確認しました。
構造化データ・schema.org・JSON-LDの違い
ここで扱う構造化データは、Webページ内の情報に種類や関係を付けたデータです。タイトルらしい文字列があっても、それが記事名なのかサイト名なのかを機械が区別できるとは限りません。そこで記事名を headline、著者を author として示します。
| 用語 | 役割 | 記事ページの例 |
|---|---|---|
| 構造化データ | 何についての情報かを整理したデータ | 記事と、その著者・公開日の関係 |
| schema.org | 種類と項目名の共通語彙 | Article、headline、author |
| JSON-LD | データを記述する形式 | JSONに @context や @type を付けて表す |
schema.orgに型があることと、Googleがその型を検索結果の特別な表示に使うことは別です。GoogleはJSON-LDのほかMicrodataとRDFaも扱います。JSON-LDは本文のHTMLと分けて生成しやすく、Googleも推奨していますが、既存のMicrodataがそれだけで無効になるわけではありません。
技術ブログなら何を入れるか
最初は、本文を正しく表せる型に絞ります。記事本文には Article またはその下位型の BlogPosting、パンくずには BreadcrumbList が候補です。著者が個人なら Person、組織なら Organization を使い、公開画面と同じ主体を記述します。
GoogleのArticleの仕様では必須プロパティはなく、記事名・著者・日付・画像などが推奨されています。存在しない著者や画像を補って警告を消すのではなく、実際に確認できる情報を出してください。タイトルを機械的に110文字で切る必要もありません。
著者
表示している執筆主体と型・名前を一致させます。著者と運営会社が違うなら、authorとpublisherを混同しません。
日付
公開日と実質的な更新日を使います。アクセス数の増加やバックアップ実行時刻を記事の更新日にしません。
画像とURL
記事に対応した公開画像と正規URLを使います。開発環境のURLや、ログインしないと読めない画像を混ぜないよう確認します。
情報がない場合は、その任意項目を省略する方が事実と整合します。一方、画像やプロフィールを新しく用意するなら、検索向けだけでなく読者にも役立つ内容にします。
PHPでJSON-LDを生成する例
以下は、入力を配列で受け取りJSON文字列を返す例です。独自のクラスとして app/Support/ArticleJsonLd.php に置きます。フレームワークへ依存する処理を含めていないため、PHP単体でも検証できます。
<?php
namespace App\Support;
final class ArticleJsonLd
{
public static function encode(array $article): string
{
$data = [
'@context' => 'https://schema.org',
'@type' => 'BlogPosting',
'headline' => $article['title'],
'mainEntityOfPage' => $article['url'],
'datePublished' => $article['published_at']->format(DATE_ATOM),
'author' => $article['author'],
];
if (!empty($article['modified_at'])) {
$data['dateModified'] = $article['modified_at']->format(DATE_ATOM);
}
if (!empty($article['image_url'])) {
$data['image'] = [$article['image_url']];
}
return json_encode(
$data,
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
| JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT
| JSON_THROW_ON_ERROR
);
}
}
タイトル・URL・著者・日時は、呼び出す側で公開画面に使う値を渡します。日時は DateTimeImmutable やCarbonのような日時オブジェクトを想定しています。入力の存在やURLの公開可否まで、この関数が保証するわけではありません。
LaravelのBladeでは、コントローラで作った $articleJsonLd を次のように出力できます。
<script type="application/ld+json">
{!! $articleJsonLd !!}
</script>
ここでのエスケープ抑止は、上の関数を通したJSON文字列に限ります。手入力の文字列や未処理のリクエストをそのまま渡してはいけません。通常の本文や属性には、その出力先に合ったエスケープが別途必要です。
JSONとして正しいだけではHTMLに安全に埋め込めない
JSONの値に閉じスクリプトタグの文字列が入ると、HTMLを解析するブラウザはJSONの文字列内でもそこで要素を閉じる可能性があります。application/ld+json を指定しただけでは、この問題はなくなりません。これはHTML Standardのscript要素の制約に関わります。
上の例で JSON_HEX_TAG を使うのは、山括弧をJSONのUnicodeエスケープへ変換するためです。JSON_THROW_ON_ERROR は、不正な文字エンコーディングなどで失敗したとき、空の出力で見逃さず例外として検出するために付けています。フラグの意味はPHP公式のJSON定数一覧で確認できます。
掲載コードで確かめたこと
通常の日本語タイトルに加えて、引用符・アンパサンド・閉じスクリプトタグを含むタイトルを入力しました。生成結果に生の閉じスクリプトタグが残らず、JSONをデコードすると元のタイトルへ戻ることを確認しています。また、任意項目の省略と、不正UTF-8入力で例外になることも確認しました。
| 入力・条件 | 確認結果 | この検証で分からないこと |
|---|---|---|
| 日本語・引用符を含むタイトル | デコード後に元の文字列と一致 | 本文とタイトルの意味上の一致 |
| 閉じスクリプトタグを含むタイトル | 生のタグ文字列が出力に残らない | 別の出力箇所の安全性 |
| 画像・更新日を渡さない | 該当プロパティを省略 | Googleでの表示内容 |
| 不正UTF-8 | JsonExceptionで停止 | アプリ全体の例外通知・復旧 |
この検証は埋め込み処理の確認です。検索結果の表示を実証したものではありません。公開前には次の手順で、出力したページ全体も確認します。
公開前後は3段階で確認する
1. 出力されたJSON
テンプレートではなく、返されたHTMLのapplication/ld+jsonを取り出して構文を確認します。手書きの末尾カンマ、二重のHTMLエスケープ、プラグインとの重複出力を探します。
2. 語彙と検索機能
Schema Markup Validatorで型や項目を確認し、Googleのリッチリザルト テストで対応機能の問題を確認します。両方が同じ判定になるとは限りません。
3. 本文との一致
著者・日付・画像・記事名を実際の画面と突き合わせます。テストツールが成功しても、存在しない実績や非表示の評価は正当化されません。
Schema Markup Validatorはschema.orgのマークアップを確認する道具で、Google固有の表示資格を保証しません。リッチリザルト テストで対象が検出されない場合も、直ちにJSONの構文エラーとは限らず、Googleの対応機能を確認する必要があります。
たとえば著者名の問題なら、まず出力された author の中身を確認します。名前が空ならデータ取得とテンプレートを直し、組織名を個人として書いているなら型も見直します。警告を消すために架空の名前を補う対応はしません。
公開後はSearch Consoleで検出状況を追います。再クロールやレポートの反映には時間差があるため、コードを直した直後は公開HTMLとテストツールで確認し、検索の結果とは分けて記録します。
検索結果での表示終了と手動対策を混同しない
Googleの2026年5月の変更履歴では、FAQリッチリザルトは2026年5月7日から検索結果へ表示されなくなったと説明されています。検索結果を広く見せるためだけにFAQPageを追加する理由にはなりません。
ただし、FAQが多いことやFAQPageがあること自体が、直ちに手動対策の原因になるわけではありません。問題は、読者に見えない情報や実態と違う内容をマークアップすることなどです。Googleの一般ガイドラインに沿って、本文とマークアップを点検します。「何問以下なら安全」といった根拠のない線引きもしません。
読者の疑問に答える質問と回答は残せます。表示されなくなったことを理由に本文まで消す必要はありません。手動対策の有無はSearch Consoleの該当レポートで確認し、単なる非表示から違反を推測しないでください。
AI検索の引用を増やすための必須設定なのか
Googleは、AI OverviewsやAI Mode向けに特別なschema.orgの構造化データを追加する必要はないと公式ガイドで説明しています。既存の構造化データを本文と一致させることは必要ですが、「特定の型を足せば引用が増える」とは言えません。
この説明を、すべてのAIサービスに共通する仕様として広げることもできません。LLMO施策として扱う場合も、どのサービスのどの結果を測るかを先に決めます。本文の改善とマークアップ変更を同時に行ったなら、流入が増えても片方だけの成果とは断定できません。
実装の優先順位は、読者に必要な本文、たどれる内部リンク、ページの安定した表示を整え、その内容を構造化データにも反映する順です。検索やAIへの表示が増えなくても、読者に残る価値を先に作ります。
よくある質問
構造化データを入れると順位は上がりますか?
順位上昇を保証する設定ではありません。対応する検索機能の候補になることと、実際に表示されることを分けて考えます。テスト合格を検索成果として報告しないことも大切です。
JSON-LDだけ書けばHTML本文は短くてもよいですか?
本文の代わりにはなりません。構造化データは、読者に示している内容と一致させます。コード側だけに説明や評価を追加しても、本文の不足は解決しません。
リッチリザルト テストでエラーがゼロなら完了ですか?
構文や対応機能の確認に加え、本文との一致、画像やURLへのアクセスを確認します。ツールがポリシー上の問題をすべて見つけるわけではありません。
更新日にはDBのupdated_atを使えばよいですか?
その値が記事の実質的な更新を表すなら使えます。閲覧数や保守処理でも変わる設計なら、そのまま使わず、本文の更新を示す日時を別に管理します。過去の更新時刻が不明な場合は推測で補いません。