WordPressサイトのSEO改善をしようとすると、「とりあえずSEOプラグインを入れておけばいい」という判断をしがちです。

実際、VK All in One Expansion Unit(VK ExUnit)というプラグインで構造化データを出力していましたが、ソースを確認してみると意外と内容が薄く、必要な情報が過不足なく出力されているとは言えない状況でした。

この記事では、head.php に構造化データを自前で実装することで、どのような問題を解決したかを実例とコードつきで解説します。


    プラグイン任せの構造化データの実態

    まず、VK ExUnit で「構造化データ - WebSite」にチェックを入れていた状態でソースを確認しました。

    {"@context":"https://schema.org/","@type":"WebSite","name":"CODE-PLUS","url":"https://code-plus.jp/gp"}
    

    出力されてはいます。ただし descriptionpotentialAction(SearchAction)も何もない、最低限の2プロパティだけの状態でした。


    対応方針:structured-data.php に一元化する

    head.php に直接べた書きしていくと管理が煩雑になるため、commons/structured-data.php というファイルを新設して主要な構造化データをまとめることにしました。

    VK ExUnit 側は「構造化データ - WebSite / 記事 / パンくずリスト」の3つをすべてOFFにして、head.php 側で管理する構成に切り替えます。


    実装① canonical タグ

    https://code-plus.jp/gphttps://code-plus.jp/gp/ のどちらでアクセスしても同じページが表示される場合、Googleが別ページとして認識して評価が分散する可能性があります。canonical を明示することでどちらが正規URLかを伝えます。

    /* structured-data.php */
    
    <!-- ▼SEO:canonical -->
    <?php
    $canonical_url = '';
    if (is_front_page()) {
      $canonical_url = trailingslashit(home_url());
    } elseif (is_singular()) {
      $canonical_url = get_permalink();
    } elseif (is_category() || is_tag() || is_tax()) {
      $term_link = get_term_link(get_queried_object());
      if (!is_wp_error($term_link)) {
        $canonical_url = $term_link;
      }
    }
    if ($canonical_url) : ?>
      <link rel="canonical" href="<?php echo esc_url($canonical_url); ?>">
    <?php endif; ?>
    

    実装② WebSite + SearchAction(TOPページ)

    TOPページにだけ出力します。potentialAction に SearchAction を追加することで、Google の検索結果にサイト内検索ボックス(サイトリンク検索ボックス)が表示される可能性が出てきます。

    description は TOPページの「抜粋(meta description と同じ)」から動的取得することで、抜粋を書き換えるだけで description と構造化データが同時に更新される設計にしています。

    <?php if (is_front_page()) : ?>
    <?php
    global $post;
    $ld_description = ($post && has_excerpt()) ? get_the_excerpt() : '';
    ?>
    <script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "WebSite",
      "name": "<?php echo esc_js(get_bloginfo('name')); ?>",
      "url": "<?php echo esc_url(trailingslashit(home_url())); ?>"
      <?php if ($ld_description) : ?>
      ,"description": "<?php echo esc_js($ld_description); ?>"
      <?php endif; ?>
      ,"inLanguage": "ja"
      ,"potentialAction": {
        "@type": "SearchAction",
        "target": {
          "@type": "EntryPoint",
          "urlTemplate": "<?php echo esc_url(home_url()); ?>/?s={search_term_string}"
        },
        "query-input": "required name=search_term_string"
      }
    }
    </script>
    <?php endif; ?>
    

    実装③ TechArticle(記事ページ)

    記事ページには TechArticle スキーマを使います。Article でも間違いではありませんが、CODE PLUS のコンテンツは技術手順・実装解説が中心なので TechArticle の方が実態に近く、リッチリザルトで「技術情報」ラベルが表示されやすくなります。

    アイキャッチ画像・公開日・更新日・著者名・サイト名をすべて含めた形で出力します。

    <?php elseif (is_single() && get_post_type() === 'post') : ?>
    <?php
    $article_title    = get_the_title();
    $article_url      = get_permalink();
    $article_date     = get_the_date('c');
    $article_modified = get_the_modified_date('c');
    $article_desc     = ($post && has_excerpt()) ? get_the_excerpt() : '';
    $author_name      = get_the_author();
    $site_name        = get_bloginfo('name');
    $site_url         = trailingslashit(home_url());
    $thumbnail_url    = has_post_thumbnail() ? get_the_post_thumbnail_url(null, 'large') : '';
    ?>
    <script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "TechArticle",
      "headline": "<?php echo esc_js($article_title); ?>",
      "url": "<?php echo esc_url($article_url); ?>"
      <?php if ($article_desc) : ?>
      ,"description": "<?php echo esc_js($article_desc); ?>"
      <?php endif; ?>
      <?php if ($thumbnail_url) : ?>
      ,"image": "<?php echo esc_url($thumbnail_url); ?>"
      <?php endif; ?>
      ,"datePublished": "<?php echo esc_attr($article_date); ?>"
      ,"dateModified": "<?php echo esc_attr($article_modified); ?>"
      ,"author": {"@type": "Person", "name": "<?php echo esc_js($author_name); ?>"}
      ,"publisher": {"@type": "Organization", "name": "<?php echo esc_js($site_name); ?>", "url": "<?php echo esc_url($site_url); ?>"}
      ,"inLanguage": "ja"
    }
    </script>
    <?php endif; ?>
    

    実装④ BreadcrumbList(パンくず構造化データ)

    パンくずリストは UI として表示するだけでなく、構造化データとしてGoogleに伝えることで検索結果上にパンくずが表示されるようになります。

    breadcrumb.php の表示ロジックと多くの対応させています。

    ページ種別 パンくず構造
    投稿ページ トップ > 投稿一覧 > カテゴリー > 記事名
    カスタム投稿 トップ > カスタム投稿一覧 > カテゴリー > 記事名
    アーカイブ トップ > アーカイブ
    固定ページ トップ > (先祖ページ) > 現在ページ
    TOP・404・検索結果 出力しない
    <!-- ▼SEO:BreadcrumbList(TOPページ・404・検索結果以外) -->
    <?php if (!is_front_page() && !is_404() && !is_search()) : ?>
    <?php
    global $post;
    $home_url = trailingslashit(do_shortcode('[tophomeurl]'));
    $bc_items = [];
    $position = 1;
    
    $bc_items[] = ['pos' => $position++, 'name' => 'トップ', 'url' => $home_url];
    
    if (is_single() && get_post_type() === 'post') {
      $bc_items[] = ['pos' => $position++, 'name' => '投稿一覧', 'url' => $home_url . 'postlist/'];
      $cat_slug = do_shortcode('[cat_slug]');
      $cat_name = do_shortcode('[cat_name]');
      if ($cat_slug && $cat_name) {
        $bc_items[] = ['pos' => $position++, 'name' => wp_strip_all_tags($cat_name), 'url' => $home_url . 'category/' . $cat_slug . '/'];
      }
      $bc_items[] = ['pos' => $position++, 'name' => wp_strip_all_tags(get_the_title()), 'url' => get_permalink()];
      // カスタム投稿・アーカイブ・固定ページのパターンは省略
    }
    
    $ld_items = [];
    foreach ($bc_items as $item) {
      $ld_items[] = '{"@type":"ListItem","position":' . $item['pos'] . ',"name":"' . esc_js($item['name']) . '","item":"' . esc_url($item['url']) . '"}';
    }
    ?>
    <script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "BreadcrumbList",
      "itemListElement": [<?php echo implode(',', $ld_items); ?>]
    }
    </script>
    <?php endif; ?>
    

    meta description の運用設計

    VK ExUnit の「メタディスクリプションタグを出力」がONの場合、抜粋があると VK ExUnit が <meta name="description"> を出力します。便利な機能ですが、head.php 側でも出力するコードがあると重複出力になります。

    以下の条件分岐で、「抜粋がない場合のみ head.php 側が出力する」構成にしました。

    <?php
    // 抜粋がある → VK ExUnitが出力するので head.php 側は出力しない
    // 抜粋がない → 「○○ページです。」を自動生成して出力
    $auto_desc = generate_meta_description();
    if (!($post && has_excerpt()) && !empty($auto_desc)) : ?>
      <meta name="description" content="<?php echo esc_attr($auto_desc); ?>">
    <?php endif; ?>
    

    これで description の管理フローはシンプルになります。

    設定 運用方法
    キャッチフレーズ(一般設定) 常に空のまま
    TOPページ title VK ExUnit の「titleタグ設定」入力欄で管理
    全ページ description 抜粋に書く。未記入なら「○○ページです。」が自動生成
    WebSite description TOPページの抜粋から動的取得(= meta description と同じ内容)

    実装後の確認方法

    Google リッチリザルトテスト にURLを貼り付けると、構造化データが正しく認識されているか確認できます。記事ページでは TechArticle と BreadcrumbList の両方が検出されれば成功です。

    SearchAction の反映(サイトリンク検索ボックス)は数週間〜数ヶ月かかるので、実装後はしばらく様子を見ます。


    まとめ

    「SEOプラグインを入れている = 対策済み」という状態から、structured-data.php に一元化することで以下が整いました。

    • ✅ canonical タグ(全ページ)
    • ✅ WebSite + SearchAction(TOPページ)
    • ✅ TechArticle(記事ページ)
    • ✅ BreadcrumbList(全ページ・条件分岐あり)
    • ✅ meta description の重複解消と運用設計

    実装量はそれほど多くありませんが、ソースを確認する前と後では、Googleに伝えられる情報量が全然違います。まだ構造化データを入れていないWordPressサイトがあれば、まずソースを確認してみることをお勧めします。