2026年5月20日
2026年5月20日
WordPressのJSON-LD構造化データを実装する方法
はじめに
JSON-LD(JavaScript Object Notation for Linked Data)はGoogleが推奨する構造化データの記述形式です。正しく実装するとGoogleの検索結果にパンくずリスト・FAQの展開・レビュースター・イベント情報などの「リッチリザルト」が表示され、クリック率が大幅に向上します。
症状・原因
- Google Search ConsoleでリッチリザルトのエラーやWARNINGが出ている
- 構造化データを設定したのに検索結果に反映されない
- Yoast SEOなどプラグインの構造化データでは対応できないカスタム要件がある
- FAQや会社情報を構造化データで出力したい
解決手順
ステップ1:JSON-LDの基本構造を理解する
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "記事タイトル",
"datePublished": "2024-01-15T10:00:00+09:00",
"dateModified": "2024-01-20T15:30:00+09:00",
"author": {
"@type": "Person",
"name": "著者名",
"url": "https://example.com/author/user/"
},
"publisher": {
"@type": "Organization",
"name": "サイト名",
"logo": {
"@type": "ImageObject",
"url": "https://example.com/logo.png"
}
},
"image": "https://example.com/ogp-image.jpg",
"mainEntityOfPage": "https://example.com/post-slug/"
}
ステップ2:Articleスキーマをwp_headで出力する
// functions.php
function mytheme_json_ld_article(): void {
if (!is_singular('post')) {
return;
}
$post = get_post();
$author_id = (int) $post->post_author;
$published = get_the_date('c', $post);
$modified = get_the_modified_date('c', $post);
$permalink = get_permalink($post);
$site_name = get_bloginfo('name');
$site_url = home_url('/');
// アイキャッチ画像
$image_url = '';
if (has_post_thumbnail($post)) {
$src = wp_get_attachment_image_src(
get_post_thumbnail_id($post),
'large'
);
if ($src) {
$image_url = $src[0];
}
}
// ロゴ画像(カスタマイザーで設定したロゴ)
$logo_url = '';
$logo_id = get_theme_mod('custom_logo');
if ($logo_id) {
$logo_src = wp_get_attachment_image_src($logo_id, 'full');
if ($logo_src) {
$logo_url = $logo_src[0];
}
}
$schema = [
'@context' => 'https://schema.org',
'@type' => 'Article',
'headline' => get_the_title($post),
'datePublished' => $published,
'dateModified' => $modified,
'url' => $permalink,
'mainEntityOfPage' => $permalink,
'author' => [
'@type' => 'Person',
'name' => get_the_author_meta('display_name', $author_id),
'url' => get_author_posts_url($author_id),
],
'publisher' => [
'@type' => 'Organization',
'name' => $site_name,
'url' => $site_url,
],
];
if ($image_url) {
$schema['image'] = $image_url;
}
if ($logo_url) {
$schema['publisher']['logo'] = [
'@type' => 'ImageObject',
'url' => $logo_url,
];
}
echo '<script type="application/ld+json">'
. wp_json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)
. '</script>' . "\n";
}
add_action('wp_head', 'mytheme_json_ld_article', 5);
ステップ3:BreadcrumbListスキーマを出力する
// functions.php
function mytheme_json_ld_breadcrumb(): void {
if (is_front_page() || is_home()) {
return;
}
$items = [];
$position = 1;
// トップページ
$items[] = [
'@type' => 'ListItem',
'position' => $position++,
'name' => get_bloginfo('name'),
'item' => home_url('/'),
];
// カテゴリ(投稿の場合)
if (is_singular('post')) {
$categories = get_the_category();
if (!empty($categories)) {
$cat = $categories[0];
$items[] = [
'@type' => 'ListItem',
'position' => $position++,
'name' => $cat->name,
'item' => get_category_link($cat->term_id),
];
}
// 現在の記事
$items[] = [
'@type' => 'ListItem',
'position' => $position++,
'name' => get_the_title(),
'item' => get_permalink(),
];
} elseif (is_singular('page')) {
$items[] = [
'@type' => 'ListItem',
'position' => $position++,
'name' => get_the_title(),
'item' => get_permalink(),
];
} elseif (is_category()) {
$items[] = [
'@type' => 'ListItem',
'position' => $position++,
'name' => single_cat_title('', false),
'item' => get_category_link(get_queried_object_id()),
];
}
$schema = [
'@context' => 'https://schema.org',
'@type' => 'BreadcrumbList',
'itemListElement' => $items,
];
echo '<script type="application/ld+json">'
. wp_json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
. '</script>' . "\n";
}
add_action('wp_head', 'mytheme_json_ld_breadcrumb', 5);
ステップ4:FAQPageスキーマをショートコードで出力する
// functions.php
// 使い方: [faq] Q: 質問1 A: 回答1 Q: 質問2 A: 回答2 [/faq]
function mytheme_faq_shortcode(array $atts, string $content = ''): string {
$faqs = [];
// Q: と A: のペアをパース
preg_match_all('/Q:\s*(.+?)\s*A:\s*(.+?)(?=Q:|$)/s', $content, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$faqs[] = [
'@type' => 'Question',
'name' => trim($match[1]),
'acceptedAnswer' => [
'@type' => 'Answer',
'text' => trim($match[2]),
],
];
}
if (empty($faqs)) {
return '';
}
$schema = [
'@context' => 'https://schema.org',
'@type' => 'FAQPage',
'mainEntity' => $faqs,
];
$html = '<script type="application/ld+json">'
. wp_json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
. '</script>';
// HTML側のFAQアコーディオン
foreach ($matches as $match) {
$q = esc_html(trim($match[1]));
$a = esc_html(trim($match[2]));
$html .= "<div class=\"faq-item\"><h3 class=\"faq-question\">{$q}</h3><div class=\"faq-answer\"><p>{$a}</p></div></div>";
}
return $html;
}
add_shortcode('faq', 'mytheme_faq_shortcode');
ステップ5:Google Search Consoleでリッチリザルトをテストする
リッチリザルトのテスト手順:
1. Google リッチリザルトテスト
https://search.google.com/test/rich-results
URLまたはHTMLコードを貼り付けてテスト
2. Schema.org バリデーター
https://validator.schema.org/
より詳細なスキーマ検証が可能
3. curlでJSON-LDを確認
# JSON-LDタグを確認
curl -s https://example.com/post/ | grep -A 50 'application/ld+json'
# Google クローラーのUAでアクセス確認
curl -s -A "Googlebot/2.1 (+http://www.google.com/bot.html)" https://example.com/post/ \
| grep 'ld+json'
注意事項
headlineは110文字以内にしてください。Googleのガイドラインで推奨されていますdatePublishedとdateModifiedはISO8601形式(get_the_date('c'))で出力します。タイムゾーンを含めること(+09:00)- FAQスキーマはページ内にFAQのHTML表示も必要です。構造化データだけでは不十分でGoogleに非準拠と判定されます
wp_json_encode()はjson_encode()より安全です。WordPressのフィルターが適用され、UTF-8の処理も適切に行われます
まとめ
wp_head priority:5 で Article と BreadcrumbList のJSON-LDをスクリプトタグとして出力します。FAQは [faq] ショートコードで記事内に埋め込み、スキーマとHTMLを同時生成します。実装後はGoogleのリッチリザルトテストツールで検証し、Search Consoleの「拡張」レポートでインデックス状況を確認します。