2026年5月20日

2026年5月20日

WordPressのパンくずリストの構造化データを設定する方法

はじめに

パンくずリスト(BreadcrumbList)の構造化データを実装すると、Google検索結果にURLの代わりにサイト階層(例:ホーム › カテゴリ › 記事名)が表示されます。クリック率(CTR)の向上とサイト構造の明確化に役立ちます。

症状・原因

  • 検索結果にパンくずリストのリッチリザルトが表示されない
  • パンくずリストのHTMLはあるが構造化データが未設定
  • Yoast SEOのパンくずリストとカスタムパンくずが競合している
  • カテゴリ階層が深くパンくずが正しく表示されない

解決手順

ステップ1:BreadcrumbListの仕様を理解する

【BreadcrumbList 構造化データの基本形】

{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "name": "ホーム",
      "item": "https://example.com/"
    },
    {
      "@type": "ListItem",
      "position": 2,
      "name": "WordPressの使い方",
      "item": "https://example.com/category/wordpress/"
    },
    {
      "@type": "ListItem",
      "position": 3,
      "name": "パンくずリストの設定方法"
      // 最後の要素には item が不要(現在ページ)
    }
  ]
}

【ルール】
- position は1から始まる連番
- item は絶対URL(最後の要素は省略可)
- name はページの表示名

ステップ2:JSON-LDでBreadcrumbListを実装する

// functions.php: BreadcrumbList 構造化データを自動生成

add_action('wp_head', function(): void {
    $items = get_breadcrumb_items();
    if (empty($items)) return;

    $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>' . PHP_EOL;
}, 5);

function get_breadcrumb_items(): array {
    $items    = [];
    $position = 1;

    // ① ホーム(常に含める)
    $items[] = [
        '@type'    => 'ListItem',
        'position' => $position++,
        'name'     => 'ホーム',
        'item'     => home_url('/'),
    ];

    if (is_front_page() || is_home()) {
        return $items;
    }

    // ② カテゴリページ
    if (is_category()) {
        $cat = get_queried_object();
        // 親カテゴリが存在する場合は階層を辿る
        if ($cat->parent) {
            $ancestors = array_reverse(get_ancestors($cat->term_id, 'category'));
            foreach ($ancestors as $ancestor_id) {
                $ancestor = get_term($ancestor_id, 'category');
                $items[]  = [
                    '@type'    => 'ListItem',
                    'position' => $position++,
                    'name'     => $ancestor->name,
                    'item'     => get_category_link($ancestor_id),
                ];
            }
        }
        $items[] = [
            '@type'    => 'ListItem',
            'position' => $position,
            'name'     => $cat->name,
            // 現在ページは item 省略
        ];
        return $items;
    }

    // ③ 投稿ページ
    if (is_singular('post')) {
        // メインカテゴリを取得
        $categories = get_the_category();
        if (!empty($categories)) {
            $cat = $categories[0];
            // 親カテゴリの階層を追加
            if ($cat->parent) {
                $ancestors = array_reverse(get_ancestors($cat->term_id, 'category'));
                foreach ($ancestors as $ancestor_id) {
                    $ancestor = get_term($ancestor_id, 'category');
                    $items[]  = [
                        '@type'    => 'ListItem',
                        'position' => $position++,
                        'name'     => $ancestor->name,
                        'item'     => get_category_link($ancestor_id),
                    ];
                }
            }
            $items[] = [
                '@type'    => 'ListItem',
                'position' => $position++,
                'name'     => $cat->name,
                'item'     => get_category_link($cat->term_id),
            ];
        }
        $items[] = [
            '@type'    => 'ListItem',
            'position' => $position,
            'name'     => get_the_title(),
        ];
        return $items;
    }

    // ④ 固定ページ(階層対応)
    if (is_page()) {
        global $post;
        if ($post->post_parent) {
            $ancestors = array_reverse(get_post_ancestors($post->ID));
            foreach ($ancestors as $ancestor_id) {
                $items[] = [
                    '@type'    => 'ListItem',
                    'position' => $position++,
                    'name'     => get_the_title($ancestor_id),
                    'item'     => get_permalink($ancestor_id),
                ];
            }
        }
        $items[] = [
            '@type'    => 'ListItem',
            'position' => $position,
            'name'     => get_the_title(),
        ];
        return $items;
    }

    return $items;
}

ステップ3:HTMLパンくずリストと構造化データを同期する

// functions.php: HTMLパンくずを出力する関数
// 構造化データと同じデータを共有

function the_breadcrumb(): void {
    $items = get_breadcrumb_items();
    if (count($items) <= 1) return;

    echo '<nav aria-label="パンくずリスト" class="breadcrumb">';
    echo '<ol itemscope itemtype="https://schema.org/BreadcrumbList">';

    foreach ($items as $item) {
        $is_last = ($item['position'] === count($items));
        echo '<li itemprop="itemListElement" itemscope'
             . ' itemtype="https://schema.org/ListItem">';

        if (!$is_last && isset($item['item'])) {
            echo '<a itemprop="item" href="' . esc_url($item['item']) . '">';
            echo '<span itemprop="name">' . esc_html($item['name']) . '</span></a>';
        } else {
            echo '<span itemprop="name">' . esc_html($item['name']) . '</span>';
        }

        echo '<meta itemprop="position" content="' . esc_attr($item['position']) . '">';
        echo '</li>';
        if (!$is_last) echo '<li class="separator" aria-hidden="true">›</li>';
    }

    echo '</ol></nav>';
}

// テーマのheader.phpやsingle.phpで使用:
// <?php the_breadcrumb(); ?>

ステップ4:Yoast SEOのパンくず設定を使う

// Yoast SEO のパンくずリストを有効化
// Yoast SEO → 検索の見え方 → パンくずリスト → 有効

// テーマでの使用:
// if (function_exists('yoast_breadcrumb')) {
//     yoast_breadcrumb('<nav class="breadcrumb">', '</nav>');
// }

// Yoast SEO のパンくずをカスタマイズ
add_filter('wpseo_breadcrumb_single_link', function(string $link, array $element): string {
    // ホームのラベルを変更
    if ($element['url'] === home_url('/')) {
        $link = str_replace('>Home<', '>トップ<', $link);
    }
    return $link;
}, 10, 2);

// パンくずの区切り文字を変更
add_filter('wpseo_breadcrumb_separator', function(): string {
    return ' › ';
});

// Yoast SEO のパンくず JSON-LD 出力を確認
// → wp_head に自動で出力される(手動実装と重複しないよう注意)
add_action('wp_head', function(): void {
    if (defined('WPSEO_VERSION')) {
        // Yoast SEO が有効な場合は手動実装を停止する
        remove_action('wp_head', 'output_breadcrumb_schema', 5);
    }
});

ステップ5:リッチリザルトテストで確認する

# Google リッチリザルトテスト(ブラウザ)
# https://search.google.com/test/rich-results
# URL または HTMLコードを入力して BreadcrumbList を確認

# コマンドラインで構造化データを確認
curl -s https://example.com/category/wordpress/post-slug/ \
    | grep -A 30 '"BreadcrumbList"'

# Search Console で確認
# Search Console → 拡張機能 → パンくずリスト
# → エラーと有効なアイテム数を確認

注意事項

  • Yoast SEOやRank Mathプラグインを使用している場合、これらが自動でBreadcrumbListを出力します。手動実装と重複しないように注意してください
  • itemプロパティのURLはカノニカルURLと一致させてください。リダイレクト先のURLを指定することをGoogleは推奨しています
  • 最後のパンくず(現在ページ)のitemは省略可能ですが、省略せずに正規URLを記述することも有効です

まとめ

BreadcrumbListはget_breadcrumb_items()関数でホーム→親カテゴリ→カテゴリ→記事の階層を配列で構築し、wp_headにJSON-LDとして出力します。HTMLパンくずリストも同じ関数のデータを使うことでDRY原則を維持できます。Yoast SEOを使用する場合はプラグインの自動出力と重複しないよう注意が必要です。Googleリッチリザルトテストで実装を確認し、Search Consoleのパンくずリストレポートでエラーがないことをチェックします。

お気軽にご相談ください

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