WordPressカスタムテーマのSEO最適化でやったこと全部

カスタムテーマを自作すると、Cocoonやその他の有名テーマが裏でやってくれていたSEO施策を全部自分でやる必要がある。このブログ「中野のAI開発部屋」のテーマ(nakano-theme)を作りながら、SEOのために実装した施策を全部まとめる。

なぜカスタムテーマでSEOを自前実装したか

WordPressのテーマをゼロから作ると、HTMLの構造から何から全部自分でコントロールできる一方、既製テーマが内包するSEOの恩恵は一切ない。

当初は「まあ後でやればいい」と思っていたが、Googleサーチコンソールでインデックスの状況を見ていると、構造化データなしの記事とありの記事で扱いが明らかに違うことに気づいて、真剣に取り組むことにした。

実装した施策一覧

1. titleタグとdescriptionの最適化

カスタムテーマではwp_head()に任せておけばある程度は出力されるが、SEO的に適切なフォーマットにするには自前で制御する必要がある。

function nakano_theme_document_title_parts( $title ) {
    if ( is_front_page() ) {
        $title['title'] = get_bloginfo( 'name' );
        $title['tagline'] = get_bloginfo( 'description' );
    }
    return $title;
}
add_filter( 'document_title_parts', 'nakano_theme_document_title_parts' );

記事ページでは「記事タイトル | サイト名」、カテゴリページでは「カテゴリ名 | サイト名」という形に統一した。

descriptionはAll in One SEO等のプラグインに任せる手もあるが、軽量化のため自前実装を選んだ。抜粋文がある場合はそれを、なければ本文から最初の120文字を切り出してmeta descriptionに使う。

function nakano_theme_meta_description() {
    if ( is_singular() ) {
        global $post;
        if ( $post->post_excerpt ) {
            $description = strip_tags( $post->post_excerpt );
        } else {
            $description = mb_substr( strip_tags( $post->post_content ), 0, 120 );
        }
        echo '<meta name="description" content="' . esc_attr( $description ) . '">' . "\n";
    }
}
add_action( 'wp_head', 'nakano_theme_meta_description' );

2. OGP(Open Graph Protocol)タグ

SNSでシェアされたときにタイトル・画像・説明が正しく表示されるよう、OGPタグを実装した。

function nakano_theme_ogp_tags() {
    if ( is_singular() ) {
        $post_id = get_the_ID();
        $title = get_the_title();
        $url = get_permalink();

        // アイキャッチ画像
        if ( has_post_thumbnail( $post_id ) ) {
            $img = wp_get_attachment_image_src( get_post_thumbnail_id( $post_id ), 'large' );
            $image_url = $img[0];
        } else {
            $image_url = get_template_directory_uri() . '/assets/images/default-ogp.png';
        }

        echo '<meta property="og:type" content="article">' . "\n";
        echo '<meta property="og:title" content="' . esc_attr( $title ) . '">' . "\n";
        echo '<meta property="og:url" content="' . esc_url( $url ) . '">' . "\n";
        echo '<meta property="og:image" content="' . esc_url( $image_url ) . '">' . "\n";
        echo '<meta name="twitter:card" content="summary_large_image">' . "\n";
    }
}
add_action( 'wp_head', 'nakano_theme_ogp_tags' );

3. 構造化データ(JSON-LD)

これが一番手間がかかったが、効果も大きかった。記事ページにはArticleスキーマ、トップページにはWebSiteスキーマを実装した。

function nakano_theme_structured_data() {
    if ( is_singular( 'post' ) ) {
        global $post;

        $author_name = get_the_author_meta( 'display_name', $post->post_author );
        $published   = get_the_date( 'c', $post );
        $modified    = get_the_modified_date( 'c', $post );

        $schema = [
            '@context' => 'https://schema.org',
            '@type'    => 'Article',
            'headline' => get_the_title(),
            'author'   => [
                '@type' => 'Person',
                'name'  => $author_name,
            ],
            'datePublished' => $published,
            'dateModified'  => $modified,
            'url'           => get_permalink(),
        ];

        if ( has_post_thumbnail() ) {
            $img = wp_get_attachment_image_src( get_post_thumbnail_id(), 'large' );
            $schema['image'] = $img[0];
        }

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

4. パンくずリスト(BreadcrumbList スキーマ)

パンくずリストも構造化データとして出力するようにした。カテゴリ→記事という階層を正しく表現できる。

function nakano_theme_breadcrumb_schema() {
    if ( ! is_singular( 'post' ) ) return;

    $items = [];
    $items[] = [
        '@type'    => 'ListItem',
        'position' => 1,
        'name'     => 'ホーム',
        'item'     => home_url( '/' ),
    ];

    $categories = get_the_category();
    if ( $categories ) {
        $cat = $categories[0];
        $items[] = [
            '@type'    => 'ListItem',
            'position' => 2,
            'name'     => $cat->name,
            'item'     => get_category_link( $cat->term_id ),
        ];
    }

    $items[] = [
        '@type'    => 'ListItem',
        'position' => count( $items ) + 1,
        'name'     => get_the_title(),
        'item'     => get_permalink(),
    ];

    $schema = [
        '@context'        => 'https://schema.org',
        '@type'           => 'BreadcrumbList',
        'itemListElement' => $items,
    ];

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

5. canonicalタグ

重複コンテンツ対策のcanonicalタグ。WordPressはページネーションやアーカイブで同じコンテンツが複数URLに現れることがあるため、正規URLを明示する。

function nakano_theme_canonical() {
    if ( is_singular() ) {
        echo '<link rel="canonical" href="' . esc_url( get_permalink() ) . '">' . "\n";
    } elseif ( is_front_page() ) {
        echo '<link rel="canonical" href="' . esc_url( home_url( '/' ) ) . '">' . "\n";
    }
}
add_action( 'wp_head', 'nakano_theme_canonical' );

6. 画像のalt属性とlazy loading

記事内の画像すべてにalt属性を設定し、loading="lazy"を付けるようにした。the_contentフィルターで一括処理している。

function nakano_theme_add_image_attrs( $content ) {
    // loading="lazy" を追加(既にある場合はスキップ)
    $content = preg_replace(
        '/<img(?![^>]*loading=)([^>]*)>/i',
        '<img loading="lazy"$1>',
        $content
    );
    return $content;
}
add_filter( 'the_content', 'nakano_theme_add_image_attrs' );

7. XML サイトマップ

WordPress 5.5以降は標準でXMLサイトマップが生成されるが、カスタムテーマでもwp_sitemaps_enabledフィルターで有効化されていることを確認しておく必要がある。

固定ページや投稿以外にカスタム投稿タイプを追加した場合は、サイトマップにも含まれるよう設定を追加する。

8. robots.txtの最適化

WordPress標準のrobots.txtは最低限の設定しかないため、/wp-admin/へのクロールをブロックし、Sitemap:ディレクティブを追記する。

User-agent: *
Disallow: /wp-admin/
Allow: /wp-admin/admin-ajax.php
Sitemap: https://obaba-win.com/wp-sitemap.xml

WordPressの管理画面から設定できるが、REST API経由でも操作できる。

実装して気づいたこと

カスタムテーマのSEO実装で一番大変だったのは、「何が漏れているかわからない」という点だった。Cocoonのような多機能テーマは数百のSEO施策が内包されていて、それを自分で一つ一つ把握して実装する必要がある。

Googleサーチコンソールの「URL検査」ツールで実際にGooglebotがどう読んでいるかを確認しながら実装を進めたのが効果的だった。構造化データのエラーは「リッチリザルトテスト」ですぐ確認できるのでおすすめ。

まとめ

カスタムWordPressテーマのSEO最適化としてやったことをまとめると:

  • titleタグ・descriptionの最適化
  • OGPタグ(og:title, og:image, Twitter Cardなど)
  • Article構造化データ(JSON-LD)
  • BreadcrumbListスキーマ
  • canonicalタグ
  • 画像のalt属性・lazy loading
  • XMLサイトマップの確認
  • robots.txtの最適化

プラグインに頼らず実装したことで、不要なJavaScriptやCSSの読み込みがなくなり、PageSpeed Insightsのスコアも上がった。テーマを自作するなら、このあたりを最初から設計に組み込んでおくといい。