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のガイドラインで推奨されています
  • datePublisheddateModified はISO8601形式(get_the_date('c'))で出力します。タイムゾーンを含めること(+09:00
  • FAQスキーマはページ内にFAQのHTML表示も必要です。構造化データだけでは不十分でGoogleに非準拠と判定されます
  • wp_json_encode()json_encode() より安全です。WordPressのフィルターが適用され、UTF-8の処理も適切に行われます

まとめ

wp_head priority:5 で ArticleBreadcrumbList のJSON-LDをスクリプトタグとして出力します。FAQは [faq] ショートコードで記事内に埋め込み、スキーマとHTMLを同時生成します。実装後はGoogleのリッチリザルトテストツールで検証し、Search Consoleの「拡張」レポートでインデックス状況を確認します。

お気軽にご相談ください

お見積りへ お問い合わせへ