閉じる

WordPressでパンくずリストを自作する方法:SEOプラグインを使わない軽量化と構造化データ対応の解説

WordPressサイトを運用する上で、パンくずリストはサイトの構造をユーザーと検索エンジンに正しく伝えるための必須要素です。

多くの開発者がSEOプラグインの標準機能を利用していますが、特定の表示要件を満たすためや、サイトの表示速度を極限まで高速化するために、プラグインを使わず自作したいというニーズが増えています。

2026年現在の検索エンジン最適化において、パンくずリストは単なるナビゲーション以上の価値を持っており、特にモバイルユーザーの利便性向上に大きく寄与します。

プラグインに頼らずにパンくずリストを実装することで、不要なコードの読み込みを排除し、サイト全体のパフォーマンスを向上させることが可能です。

本記事では、WordPressのテンプレートファイルを編集し、軽量かつ構造化データに対応したパンくずリストを自作する具体的な手順を詳しく解説します。

SEOプラグインを使わずにパンくずリストを実装するメリット

まず最初に、なぜ高機能なSEOプラグインを使用せずにパンくずリストを自作するのか、その理由を明確にしておきましょう。

最大のメリットは、サーバーへの負荷を軽減し、ページの読み込み速度を向上させられる点にあります。

多くのSEOプラグインは、パンくずリスト機能以外にも膨大な機能を備えているため、プラグインを有効化するだけでデータベースへのクエリが増加し、PHPのメモリ消費量が増えてしまいます。

自作のコードであれば、必要な機能だけに絞り込むことができるため、最小限のPHP処理でパンくずリストを出力することが可能です。

また、デザインの自由度が高いことも大きな利点です。

プラグインが出力するHTMLは構造が決まっていることが多く、独自のCSSクラスを付与したり、HTML構造を大幅に変更したりすることが難しい場合があります。

自作実装であれば、あなたのサイトのデザインシステムに完全に適合したマークアップを行うことができます。

さらに、セキュリティリスクの低減も無視できません。

サードパーティ製のプラグインを減らすことは、脆弱性を突かれるリスクを減らすことにつながり、サイトの保守管理を容易にします。

これらの理由から、WordPressの内部構造を理解している制作者にとって、パンくずリストの自作は非常に合理的な選択肢となります。

パンくずリストに求められる2026年現在の要件

パンくずリストを実装する際には、単にリンクを並べるだけでは不十分です。

現代のウェブサイト制作においては、以下の3つの要素を同時に満たす必要があります。

要素詳細
ユーザビリティ現在地を直感的に把握でき、親階層へ1クリックで戻れること
構造化データ対応JSON-LD形式などで検索エンジンに階層構造を正確に伝えること
レスポンシブ対応スマートフォンなどの小さな画面でも崩れず、タップしやすいサイズであること

特に重要なのが、Googleの推奨する「Schema.org」に基づいた構造化データの実装です。

構造化データを正しく記述することで、検索結果(SERPs)にパンくずリストが表示されるようになり、クリック率(CTR)の向上が期待できます。

本記事で紹介するコードは、この構造化データを自動的に生成する仕組みを組み込んでいます。

functions.phpへの基本ロジックの実装

それでは、具体的な実装に入りましょう。

まずは、サイト内のあらゆるページで共通して利用できるパンくずリスト生成関数をfunctions.phpに定義します。

この関数では、現在の投稿タイプやカテゴリー、アーカイブページなどの条件分岐を行い、適切な階層を配列に格納していきます。

PHP
/**
 * WordPress 自作パンくずリスト生成関数
 */
function get_custom_breadcrumbs() {
    // グローバル変数の宣言
    global $post;

    // パンくずのリストを格納する配列
    $breadcrumbs = array();

    // ホーム(トップページ)の情報
    $breadcrumbs[] = array(
        'name' => 'ホーム',
        'url'  => home_url('/')
    );

    // 固定ページの場合
    if ( is_page() && !$post->post_parent ) {
        // 親ページがない固定ページ
        $breadcrumbs[] = array(
            'name' => get_the_title(),
            'url'  => ''
        );
    } elseif ( is_page() && $post->post_parent ) {
        // 親ページがある固定ページ(階層を遡る)
        $ancestors = array_reverse(get_post_ancestors($post->ID));
        foreach ($ancestors as $ancestor) {
            $breadcrumbs[] = array(
                'name' => get_the_title($ancestor),
                'url'  => get_permalink($ancestor)
            );
        }
        $breadcrumbs[] = array(
            'name' => get_the_title(),
            'url'  => ''
        );
    }

    // 投稿ページ(カテゴリー)の場合
    if ( is_single() && !is_attachment() ) {
        if ( get_post_type() != 'post' ) {
            // カスタム投稿タイプの場合
            $post_type = get_post_type_object(get_post_type());
            $breadcrumbs[] = array(
                'name' => $post_type->labels->name,
                'url'  => get_post_type_archive_link(get_post_type())
            );
        } else {
            // 通常の投稿(カテゴリーを表示)
            $category = get_the_category();
            if ( !empty($category) ) {
                $cat = $category[0];
                $cat_ancestors = array_reverse(get_ancestors($cat->term_id, 'category'));
                foreach ($cat_ancestors as $ancestor) {
                    $breadcrumbs[] = array(
                        'name' => get_cat_name($ancestor),
                        'url'  => get_category_link($ancestor)
                    );
                }
                $breadcrumbs[] = array(
                    'name' => $cat->name,
                    'url'  => get_category_link($cat->term_id)
                );
            }
        }
        // 記事タイトル
        $breadcrumbs[] = array(
            'name' => get_the_title(),
            'url'  => ''
        );
    }

    // カテゴリーアーカイブの場合
    if ( is_category() ) {
        $cat = get_queried_object();
        $cat_ancestors = array_reverse(get_ancestors($cat->term_id, 'category'));
        foreach ($cat_ancestors as $ancestor) {
            $breadcrumbs[] = array(
                'name' => get_cat_name($ancestor),
                'url'  => get_category_link($ancestor)
            );
        }
        $breadcrumbs[] = array(
            'name' => $cat->name,
            'url'  => ''
        );
    }

    // タグアーカイブ
    if ( is_tag() ) {
        $breadcrumbs[] = array(
            'name' => single_tag_title('', false),
            'url'  => ''
        );
    }

    // 検索結果ページ
    if ( is_search() ) {
        $breadcrumbs[] = array(
            'name' => '「' . get_search_query() . '」の検索結果',
            'url'  => ''
        );
    }

    // 404ページ
    if ( is_404() ) {
        $breadcrumbs[] = array(
            'name' => '404 Not Found',
            'url'  => ''
        );
    }

    return $breadcrumbs;
}

このコードのポイントは、get_post_ancestorsget_ancestorsを使用して、親階層を再帰的に取得している点です。

これにより、深い階層のカテゴリーや固定ページであっても、正確なパスを表示することができます。

また、カスタム投稿タイプにも対応しており、アーカイブページへのリンクを自動的に取得するように設計しています。

構造化データ(JSON-LD)を統合したHTML出力

次に、先ほど作成した配列データをもとに、HTMLとJSON-LDを出力する処理を記述します。

HTML5のセマンティックなタグである<nav><ol>を使用し、アクセシビリティにも配慮します。

PHP
/**
 * パンくずリストのHTMLとJSON-LDを表示する関数
 */
function render_custom_breadcrumbs() {
    $items = get_custom_breadcrumbs();
    if ( empty($items) ) return;

    echo '<nav class="breadcrumb-container" aria-label="パンくずリスト">';
    echo '<ol class="breadcrumb-list">';

    $json_items = array();
    $count = 1;

    foreach ( $items as $index => $item ) {
        $is_last = ($index === count($items) - 1);
        
        echo '<li class="breadcrumb-item">';
        if ( !$is_last && !empty($item['url']) ) {
            echo '<a href="' . esc_url($item['url']) . '">' . esc_html($item['name']) . '</a>';
        } else {
            echo '<span aria-current="page">' . esc_html($item['name']) . '</span>';
        }
        echo '</li>';

        // JSON-LD用データの生成
        $json_items[] = array(
            "@type" => "ListItem",
            "position" => $count,
            "name" => $item['name'],
            "item" => !empty($item['url']) ? esc_url($item['url']) : home_url($_SERVER['REQUEST_URI'])
        );
        $count++;
    }

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

    // JSON-LDの出力
    $json_data = array(
        "@context" => "https://schema.org",
        "@type" => "BreadcrumbList",
        "itemListElement" => $json_items
    );
    echo '<script type="application/ld+json">' . json_encode($json_data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . '</script>';
}

ここで非常に重要なのが、各リストアイテムにJSON-LDを対応させていることです。

application/ld+jsonスクリプトとして出力することで、検索エンジンはHTML上の見た目に関わらず、サイトの構造を正確に解釈できるようになります。

また、最後のアイテム(現在地)にはaria-current="page"を付与し、スクリーンリーダーを使用しているユーザーに対しても「ここが現在地である」ことを明確に伝えています。

テーマファイルでの表示設定

作成した関数を実際にサイトに表示させるには、テーマ内の適切な場所にコードを記述する必要があります。

一般的には、header.phpの最後、または各テンプレートファイル(single.php, page.php, archive.phpなど)のメインコンテンツの直前に配置します。

PHP
<?php
// パンくずリストの表示
if ( function_exists('render_custom_breadcrumbs') ) {
    render_custom_breadcrumbs();
}
?>

このように、関数が存在するかどうかをチェックしてから呼び出すことで、エラーを未然に防ぐことができます。

もし全ページ共通の場所(ヘッダー直下など)に配置したい場合は、header.phpに1か所記述するだけで完了するため、メンテナンス性も高まります。

パンくずリストを美しく見せるためのCSS

自作したパンくずリストにスタイルを適用しましょう。

パンくずリストは主張しすぎず、かつユーザーが発見しやすい控えめなデザインが好まれます。

また、モバイル環境ではリンクが横長になりやすいため、スクロールさせるか、折り返すかの対応が必要です。

CSS
.breadcrumb-container {
    background-color: #f9f9f9;
    padding: 10px 20px;
    margin-bottom: 20px;
    border-radius: 4px;
}

.breadcrumb-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    font-size: 0.9rem;
}

.breadcrumb-item {
    display: flex;
    align-items: center;
}

.breadcrumb-item + .breadcrumb-item::before {
    content: "\003E"; /* > 記号 */
    margin: 0 10px;
    color: #999;
}

.breadcrumb-item a {
    color: #0066cc;
    text-decoration: none;
}

.breadcrumb-item a:hover {
    text-decoration: underline;
}

.breadcrumb-item span {
    color: #333;
    font-weight: bold;
}

上記のCSSでは、flex-wrap: wrap;を指定することで、画面幅が狭くなった際に自動的に改行されるようにしています。

また、疑似要素::beforeを使用して、リンク間の区切り文字を表示させています。

これにより、HTMLソースを汚すことなく、清潔なマークアップを維持できます。

カスタム投稿タイプやカスタムタクソノミーへの応用

サイトによっては、標準のカテゴリーではなくカスタムタクソノミーを使用している場合もあるでしょう。

その場合は、functions.php内の条件分岐を拡張する必要があります。

具体的には、is_tax()関数を使用して現在のタクソノミーを判別し、get_term_parents_listなどで親階層を取得します。

特に「カスタム投稿タイプのアーカイブページをパンくずに入れたい」という要望は非常に多いものです。

WordPress 6.x以降(および2026年現在の環境)では、投稿タイプの設定でhas_archivetrueになっていれば、get_post_type_archive_link()で簡単にURLを取得できます。

自作の最大の強みは、こうした複雑な条件分岐を自分の思い通りにコントロールできる点にあります。

パフォーマンスをさらに向上させるための「Transient API」の活用

パンくずリストの生成には、複数のデータベースクエリ(親カテゴリーの取得、投稿タイプの取得など)が必要です。

アクセス数が多いサイトの場合、ページを表示するたびにこれらの処理を行うのは非効率な場合があります。

そこで、WordPressの「Transient API」を使用して、生成したHTMLを一時的にキャッシュする手法を推奨します。

一度生成したパンくずリストを数時間キャッシュしておけば、2回目以降のアクセスではデータベースへの負荷がゼロになります。

ただし、記事を更新した際やカテゴリーを変更した際にはキャッシュを削除(デリート)する処理も忘れずに実装してください。

よくある実装トラブルとその解決策

自作パンくずリストを実装する際、いくつかの壁に突き当たることがあります。

例えば、「複数のカテゴリーに所属している記事で、どのカテゴリーを表示すべきか」という問題です。

WordPressの標準機能では、最初に見つかったカテゴリーが優先されますが、特定の「メインカテゴリー」を表示したい場合は、そのためのカスタムフィールドやプラグイン(これだけは致し方ない場合もあります)と連携させる必要があります。

また、固定ページをフロントページ(ホームページ)に設定している場合、パンくずの起点となるURLの設定に注意してください。

home_url('/')を使用すれば基本的には問題ありませんが、サイトの階層構造によっては、ブログ一覧ページを別途挟む必要があるかもしれません。

これらの細かい挙動を一つずつ調整していくことで、プラグインでは到達できない完璧なユーザー体験を提供できるようになります。

まとめ

WordPressでパンくずリストを自作することは、サイトの軽量化とSEOの強化を同時に達成するための極めて有効な手段です。

2026年のウェブ標準において、構造化データ(JSON-LD)を正しく含めたマークアップを行うことは、検索エンジンの理解を助ける上で欠かせません。

今回解説したfunctions.phpへの実装と、適切なCSSによるスタイリングを組み合わせれば、プラグインに依存しない頑強なサイト基盤を構築できます。

プラグインを減らすことは、表示速度の改善だけでなく、将来的なWordPress本体のアップデートに伴う不具合リスクの軽減にも繋がります。

まずはシンプルな階層から実装を始め、必要に応じてカスタム投稿タイプやキャッシュ処理を追加していくのが良いでしょう。

自作ならではの柔軟性を活かし、あなたのサイトに最適化されたパンくずリストを完成させてください。

URLをコピーしました!