2026年5月20日

2026年5月20日

WordPressのパンくずリスト構造化データ(BreadcrumbList)をJSON-LDで設定する方法

パンくずリストの構造化データ(schema.org の BreadcrumbList)を出力すると、Googleがページの階層を理解しやすくなり、検索結果にURLの代わりに「サイト名 › カテゴリー › …」のような階層が表示されることがあります。この記事では、テーマに手で実装する場合のコードと、Yoast SEOやRank Mathを使っている場合に構造化データが重複しないようにする方法を説明します。

なお、2026年10月時点のGoogleのドキュメントでは、パンくずリストの表示はパソコン(デスクトップ)の検索結果で利用できる機能として説明されています。構造化データを入れても、表示されるかどうかはGoogleが判断します。

BreadcrumbListの基本形

JSON-LDでは次の形で書きます。JSONにはコメントを書けないので、説明は後の表にまとめます。

{
  "@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": "パンくずリストの設定方法"
    }
  ]
}
プロパティ 内容
itemListElement パンくずの各段階(ListItem)を順番に並べた配列。Googleは2つ以上の ListItem を求めている
position 何段目か。1から始まる連番
name パンくずに表示する名前
item その段階のページのURL。最後の段階(いま見ているページ)では省略でき、省略するとGoogleはそのページのURLを使う

実装コード:1つの関数からHTMLとJSON-LDを出す

パンくずのHTMLと構造化データを別々に組み立てると、片方だけ修正して食い違うことがあります。ここでは、階層を配列で返す関数を1つ作り、その結果をHTMLとJSON-LDの両方に使います。

テーマの functions.php、または wp-content/mu-plugins/ に置くファイルに書きます。関数名はテーマやプラグインと重ならないよう接頭辞(wpdr_)を付けています。

<?php
/**
 * パンくずの階層を配列で返す。
 * 各要素は [ 'name' => 表示名, 'url' => URL ]。最後の要素(現在のページ)は url を null にする。
 */
function wpdr_breadcrumb_items(): array {
	$plain = static function ( string $text ): string {
		return wp_strip_all_tags( html_entity_decode( $text, ENT_QUOTES, 'UTF-8' ) );
	};

	$items = array(
		array( 'name' => 'ホーム', 'url' => home_url( '/' ) ),
	);

	if ( is_front_page() ) {
		return array();
	}

	if ( is_category() ) {
		$cat = get_queried_object();
		foreach ( array_reverse( get_ancestors( $cat->term_id, 'category' ) ) as $ancestor_id ) {
			$ancestor = get_term( $ancestor_id, 'category' );
			$items[]  = array( 'name' => $plain( $ancestor->name ), 'url' => get_term_link( $ancestor ) );
		}
		$items[] = array( 'name' => $plain( $cat->name ), 'url' => null );
		return $items;
	}

	if ( is_singular( 'post' ) ) {
		$post_id    = get_queried_object_id();
		$categories = get_the_category( $post_id );
		if ( ! empty( $categories ) ) {
			$cat = $categories[0];
			foreach ( array_reverse( get_ancestors( $cat->term_id, 'category' ) ) as $ancestor_id ) {
				$ancestor = get_term( $ancestor_id, 'category' );
				$items[]  = array( 'name' => $plain( $ancestor->name ), 'url' => get_term_link( $ancestor ) );
			}
			$items[] = array( 'name' => $plain( $cat->name ), 'url' => get_term_link( $cat ) );
		}
		$items[] = array( 'name' => $plain( get_the_title( $post_id ) ), 'url' => null );
		return $items;
	}

	if ( is_page() ) {
		$post_id = get_queried_object_id();
		foreach ( array_reverse( get_post_ancestors( $post_id ) ) as $ancestor_id ) {
			$items[] = array( 'name' => $plain( get_the_title( $ancestor_id ) ), 'url' => get_permalink( $ancestor_id ) );
		}
		$items[] = array( 'name' => $plain( get_the_title( $post_id ) ), 'url' => null );
		return $items;
	}

	// 上記以外(タグ、検索結果、投稿一覧など)は出力しない
	return array();
}

/**
 * BreadcrumbList の JSON-LD を wp_head に出す。
 */
function wpdr_output_breadcrumb_schema(): void {
	// SEOプラグインが BreadcrumbList を出す環境では、二重にならないよう出力しない
	if ( defined( 'WPSEO_VERSION' ) || defined( 'RANK_MATH_VERSION' ) ) {
		return;
	}

	$items = wpdr_breadcrumb_items();
	if ( count( $items ) < 2 ) {
		return;
	}

	$list = array();
	foreach ( $items as $i => $item ) {
		$element = array(
			'@type'    => 'ListItem',
			'position' => $i + 1,
			'name'     => $item['name'],
		);
		if ( ! empty( $item['url'] ) && ! is_wp_error( $item['url'] ) ) {
			$element['item'] = $item['url'];
		}
		$list[] = $element;
	}

	$schema = array(
		'@context'        => 'https://schema.org',
		'@type'           => 'BreadcrumbList',
		'itemListElement' => $list,
	);

	echo '<script type="application/ld+json">'
		. wp_json_encode( $schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_HEX_TAG )
		. '</script>' . "\n";
}
add_action( 'wp_head', 'wpdr_output_breadcrumb_schema' );

/**
 * パンくずのHTMLを出す(テンプレートから呼ぶ)。構造化データは JSON-LD 側で出しているので、ここには microdata を付けない。
 */
function wpdr_the_breadcrumb(): void {
	$items = wpdr_breadcrumb_items();
	if ( count( $items ) < 2 ) {
		return;
	}
	echo '<nav class="breadcrumb" aria-label="パンくずリスト"><ol>';
	foreach ( $items as $item ) {
		if ( ! empty( $item['url'] ) && ! is_wp_error( $item['url'] ) ) {
			echo '<li><a href="' . esc_url( $item['url'] ) . '">' . esc_html( $item['name'] ) . '</a></li>';
		} else {
			echo '<li aria-current="page">' . esc_html( $item['name'] ) . '</li>';
		}
	}
	echo '</ol></nav>';
}

テンプレート(single.php や page.php、ブロックテーマではない場合)の、パンくずを表示したい位置に次の1行を書きます。

<?php if ( function_exists( 'wpdr_the_breadcrumb' ) ) { wpdr_the_breadcrumb(); } ?>

区切り記号(›など)はHTMLに文字として入れず、CSSの li + li::before などで付けると、読み上げソフトで余計な記号が読まれません。

コードのポイント

  • 名前の文字参照を戻す:get_the_title() が返すタイトルは、引用符などが &#8217; のような文字参照に変換されていることがあります。JSON-LDにはそのまま入れず、html_entity_decode() で元の文字に戻しています。
  • JSON_HEX_TAG:タイトルに </script> のような文字列が含まれても、scriptタグが途中で閉じないよう < と > をエスケープします。
  • カテゴリーが複数ある投稿:get_the_category() の先頭のカテゴリーを使います。どのカテゴリーを使うかを指定したい場合は、この部分を書き換えます。
  • microdataと重ねない:HTMLの側に itemscope や itemprop で同じ BreadcrumbList を書くと、同じパンくずの構造化データが2つ出力されます。この記事のコードではJSON-LDだけにしています。

Yoast SEO・Rank Mathを使っている場合

SEOプラグインを使っている場合は、プラグインのパンくず機能を使い、自前のJSON-LDは出さないのが簡単です。上のコードも、どちらかのプラグインが有効なら JSON-LD を出さないようにしています。

Yoast SEO

Yoast SEOは、パンくずリストの表示を有効にしていなくても、構造化データのグラフに BreadcrumbList を含めて出力します(2026年10月時点のプラグインのコードで確認)。そのため、Yoast SEOと自前のJSON-LDを併用すると重複します。画面にパンくずを表示したい場合は、Yoast SEOの設定でパンくずリストを有効にし、テンプレートに次のように書きます。

<?php
if ( function_exists( 'yoast_breadcrumb' ) ) {
	yoast_breadcrumb( '<nav class="breadcrumb" aria-label="パンくずリスト">', '</nav>' );
}
?>

yoast_breadcrumb() は、Yoast SEOの設定でパンくずリストが有効になっていない(またはテーマが yoast-seo-breadcrumbs のサポートを宣言していない)と何も出力しません。

Rank Math

Rank Mathは、設定でパンくずリストの機能を有効にすると、トップページ以外で BreadcrumbList の構造化データを出力します。画面への表示は、テンプレートに次のように書きます。

<?php
if ( function_exists( 'rank_math_the_breadcrumbs' ) ) {
	rank_math_the_breadcrumbs();
}
?>

Rank Mathのパンくずの構造化データだけを止めたい場合は、Rank Mathのフィルター rank_math/json_ld/breadcrumbs_enabled で false を返します。

add_filter( 'rank_math/json_ld/breadcrumbs_enabled', '__return_false' );

この場合は、上の自前のコードの defined( 'RANK_MATH_VERSION' ) の条件を外して、自前のJSON-LDを出すようにします。

よくある誤り:remove_action が効かない

「プラグインが有効なら自前の出力を止める」目的で、次のようなコードが紹介されていることがあります。

remove_action( 'wp_head', 'output_breadcrumb_schema', 5 );

remove_action() は、登録したときと同じ関数名と同じ優先度を指定しないと外せません。無名関数(add_action( 'wp_head', function () { … } ))で登録した処理は、名前で指定できないので、この方法では外せません。また、この remove_action() を wp_head の優先度10(既定)で実行する処理の中に書いた場合、その時点で優先度5の処理はすでに実行済みなので、外しても効果がありません。この記事のコードのように、出力する関数の中で条件を判定して return するほうが、意図どおりに制御できます。

出力を確認する

まず、ページのHTMLの application/ld+json の中に、BreadcrumbList が1つだけあることを確認します。

curl -s https://example.com/category/wordpress/sample-post/ | grep -o '"@type":"BreadcrumbList"' | wc -l

出力が 1 なら1つです。Yoast SEOのようにグラフ形式でまとめて出力するプラグインでは、空白を含む書き方になっていて、この方法では数えられないことがあります。その場合はブラウザーでページのソースを開いて確認します。

次に、GoogleのリッチリザルトテストにURLを入力し、「パンくずリスト」が検出され、エラーがないことを確認します。schema.orgの文法だけを確かめるならSchema Markup Validatorも使えます。

公開後は、Search Consoleのパンくずリストのレポートで、エラーや警告が出ていないかを定期的に確認します。

まとめ

  • BreadcrumbListは itemListElement に ListItem(position・name・item)を順に並べる。最後の段階の item は省略できる
  • JSON-LDにはコメントを書けない。説明はコードの外に書く
  • 階層を返す関数を1つ作り、HTMLとJSON-LDの両方で使うと食い違わない。HTML側にmicrodataを重ねない
  • Yoast SEOはパンくず表示が無効でもBreadcrumbListを出力する。Rank Mathはパンくず機能を有効にすると出力する。どちらかを使うなら自前のJSON-LDは出さない
  • 出力を止める制御は、無名関数への remove_action() ではなく、出力する関数の中の条件分岐で行う

関連記事:

お気軽にご相談ください

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