WordPressでSEOプラグインを使わずに運用していると、canonicalやOGP、構造化データ(JSON-LD)が出力されていないことがあります。

このブログ(deco8.net)も、軽さを優先してSEOプラグインを入れずに自作テーマで運用しています。先日、Webサイトのチェックツールでトップページを確認したところ、次のような指摘が出ました。

  • canonical URLが設定されていません
  • og:imageが設定されていません
  • JSON-LDが見つかりません
  • titleが長すぎる、meta descriptionが短すぎる

記事ページには対応していたものの、トップページやカテゴリーページなどの「一覧ページ」が抜けていたのが原因でした。

この記事では、そのときに実際に追加したコードをもとに、canonical・OGP・構造化データをプラグインなしで出力する方法を紹介します。

Webサイトのチェックツールで見つかった設定漏れを確認している様子

1. canonical・OGP・構造化データの役割

最初に、それぞれが何のためのものなのかを整理しておきます。

項目役割主に使われる場所
canonical同じ内容のURLが複数あるとき、正規のURLを伝える検索エンジン
title・meta descriptionページのタイトルと説明文検索結果
OGPシェアされたときのタイトル・説明文・画像Facebook・LINE・Slackなど
Twitter CardX(旧Twitter)でのカードの表示形式X
構造化データ(JSON-LD)ページの内容を機械が読める形で伝える検索エンジン
head内に出力するタグと使われる場所:titleとdescriptionとcanonicalは検索結果、OGPとTwitter CardはSNSでのシェア、JSON-LDは検索エンジンの理解に使われる

どれもページの見た目には表示されず、head内に出力するものです。そのため、設定漏れがあっても気付きにくいのが難点です。

2. 始める前に確認すること

SEOプラグインと重複しないようにする

Yoast SEO、All in One SEO、SEOPress、Rank MathなどのSEOプラグインを使っている場合は、すでにこれらのタグが出力されています。

この記事のコードを追加すると同じタグが二重に出力されてしまうため、SEOプラグインを使っている場合はプラグインの設定で対応してください。

まずはページのソースを表示して、canonicalやog:、application/ld+jsonがすでに出力されていないか確認しましょう。

コードの追加場所

コードはテーマのfunctions.phpに追加します。配布テーマを使っている場合は、テーマ更新時に消えないよう子テーマのfunctions.phpに追加してください。

関数名のmytheme_は、ほかの関数と重複しないよう、テーマ名などに合わせて変更しても大丈夫です。編集前にはfunctions.phpのバックアップを取っておきましょう。

3. canonicalを一覧ページにも出力する

WordPress本体は、投稿や固定ページなどの個別ページにはrel="canonical"を自動で出力します。

一方で、トップページ、カテゴリー・タグなどのアーカイブページ、2ページ目以降には出力されません。 チェックツールで指摘されたのもこの部分でした。

そこで、個別ページ以外にもcanonicalを出力する処理を追加します。

/**
 * ページの canonical URL を取得(クエリ文字列は含めない)
 */
function mytheme_get_canonical_url() {
	if ( is_singular() ) {
		return (string) wp_get_canonical_url();
	}
	if ( is_404() || is_search() ) {
		return '';
	}

	global $wp;
	$request = isset( $wp->request ) ? $wp->request : '';

	return $request === '' ? home_url( '/' ) : home_url( user_trailingslashit( $request ) );
}

/**
 * 一覧ページにも canonical を出力する(個別ページは WordPress 本体が出力)
 */
function mytheme_output_canonical() {
	if ( is_singular() ) {
		return;
	}
	$url = mytheme_get_canonical_url();
	if ( $url !== '' ) {
		echo '<link rel="canonical" href="' . esc_url( $url ) . '">' . "\n";
	}
}
add_action( 'wp_head', 'mytheme_output_canonical', 1 );

ポイントは次のとおりです。

  • 個別ページは本体の出力と重複しないよう、関数wp_get_canonical_url()で取得するだけにしています
  • $wp->requestにはクエリ文字列が含まれないため、?utm_source=...などのパラメータ付きでアクセスされても、パラメータなしのURLがcanonicalになります
  • 2ページ目以降は、そのページ自身のURL(/page/2/)をcanonicalにしています
  • 検索結果ページと404ページには出力しません

2ページ目以降のcanonicalを1ページ目に向けるのは、よくある間違いです。2ページ目には1ページ目と違う記事が並んでいるため、それぞれのページ自身を指定します。

参考:Google検索セントラル:重複したURLを統合する

4. トップページのtitleとmeta descriptionを調整する

title

テーマでadd_theme_support( 'title-tag' )を使っている場合、トップページのtitleは「サイト名 – キャッチフレーズ」になります。

キャッチフレーズが長いとtitleも長くなり、検索結果で途中から省略されてしまいます。トップページだけ専用のtitleにする場合は、document_title_partsフィルターを使います。

/**
 * トップページの title をキャッチフレーズではなく専用の文言にする
 */
function mytheme_front_title_parts( $parts ) {
	if ( is_front_page() ) {
		$parts['title'] = 'サイト名|サイトの内容がわかる短い説明';
		unset( $parts['tagline'], $parts['site'] );
	}
	return $parts;
}
add_filter( 'document_title_parts', 'mytheme_front_title_parts' );

2ページ目以降は「– ページ 2」のようにページ番号が自動で付きます。

meta description

meta descriptionは、ページの種類ごとに説明文を用意します。投稿ページは抜粋、カテゴリーページはカテゴリーの説明、トップページは専用の文章を使う例です。

/**
 * ページの説明文(meta description 用)を取得
 */
function mytheme_get_description() {
	if ( is_singular() && has_excerpt() ) {
		$text = get_the_excerpt();
	} elseif ( is_singular() ) {
		$text = get_post_field( 'post_content', get_queried_object_id() );
	} elseif ( is_category() || is_tag() || is_tax() ) {
		$text = term_description();
	} elseif ( is_front_page() ) {
		$text = 'トップページ用の説明文。サイトで扱っているテーマや、誰に向けたサイトなのかを書きます。';
	} else {
		$text = get_bloginfo( 'description' );
	}

	$text = wp_strip_all_tags( strip_shortcodes( (string) $text ) );
	$text = trim( preg_replace( '/\s+/u', ' ', $text ) );

	if ( mb_strlen( $text ) > 120 ) {
		$text = mb_substr( $text, 0, 120 ) . '…';
	}
	return $text;
}

カテゴリーやタグのページは、管理画面の「カテゴリー」「タグ」で説明を入力していない場合、meta descriptionが出力されません。よく見られるカテゴリーだけでも説明を入力しておくのがおすすめです。

投稿ページの説明文を本文から自動で作る方法は、こちらの記事でも紹介しています。

【WordPress】meta description をページ毎に本文から自動で挿入する方法【SEO】

5. OGPとTwitter Cardを出力する

meta description・OGP・Twitter Cardは、まとめて出力するのが簡単です。先ほどの関数mytheme_get_description()とmytheme_get_canonical_url()を使います。

/**
 * meta description・OGP・Twitter Card を出力
 */
function mytheme_output_meta() {
	if ( is_404() ) {
		return;
	}

	$description = mytheme_get_description();
	$is_post     = is_singular();
	$title       = $is_post ? get_the_title() : wp_get_document_title();
	$url         = mytheme_get_canonical_url();

	// アイキャッチがあれば使い、なければテーマ内の既定画像(1200×630)を使う
	$image = $is_post && has_post_thumbnail() ? wp_get_attachment_image_src( get_post_thumbnail_id(), 'large' ) : false;
	if ( ! $image ) {
		$image = array( get_theme_file_uri( 'images/ogp-default.jpg' ), 1200, 630 );
	}

	if ( $description !== '' ) {
		echo '<meta name="description" content="' . esc_attr( $description ) . '">' . "\n";
	}
	echo '<meta property="og:locale" content="ja_JP">' . "\n";
	echo '<meta property="og:type" content="' . ( is_singular( 'post' ) ? 'article' : 'website' ) . '">' . "\n";
	echo '<meta property="og:site_name" content="' . esc_attr( get_bloginfo( 'name' ) ) . '">' . "\n";
	echo '<meta property="og:title" content="' . esc_attr( $title ) . '">' . "\n";
	if ( $description !== '' ) {
		echo '<meta property="og:description" content="' . esc_attr( $description ) . '">' . "\n";
	}
	if ( $url !== '' ) {
		echo '<meta property="og:url" content="' . esc_url( $url ) . '">' . "\n";
	}
	echo '<meta property="og:image" content="' . esc_url( $image[0] ) . '">' . "\n";
	echo '<meta property="og:image:width" content="' . (int) $image[1] . '">' . "\n";
	echo '<meta property="og:image:height" content="' . (int) $image[2] . '">' . "\n";
	echo '<meta name="twitter:card" content="summary_large_image">' . "\n";
}
add_action( 'wp_head', 'mytheme_output_meta', 1 );

アイキャッチがないページ用の画像を用意する

トップページやカテゴリーページにはアイキャッチがないため、そのままだとog:imageが出力されません。

上のコードでは、テーマ内のimages/ogp-default.jpgを既定の画像として使っています。サイト名などを入れた1200×630ピクセルの画像を用意して、テーマのフォルダに置いてください。

X(旧Twitter)は、twitter:titleやtwitter:imageがない場合、OGPの情報を使ってカードを表示します。そのため、Twitter Cardはtwitter:cardだけ出力しています。

参考:The Open Graph protocol

6. 構造化データ(JSON-LD)を出力する

構造化データは、ページの内容を検索エンジンに伝えるためのデータです。ここでは、投稿ページに記事を表すBlogPosting、トップページにサイトを表すWebSiteを出力します。

/**
 * 構造化データ(JSON-LD)を出力
 * 投稿ページ:BlogPosting/トップページ:WebSite
 */
function mytheme_output_json_ld() {
	if ( is_singular( 'post' ) ) {
		$post_id = get_queried_object_id();
		$data    = array(
			'@context'         => 'https://schema.org',
			'@type'            => 'BlogPosting',
			'mainEntityOfPage' => get_permalink( $post_id ),
			'headline'         => get_the_title( $post_id ),
			'description'      => mytheme_get_description(),
			'datePublished'    => get_the_date( 'c', $post_id ),
			'dateModified'     => get_the_modified_date( 'c', $post_id ),
			'author'           => array(
				'@type' => 'Person',
				'name'  => get_the_author_meta( 'display_name', (int) get_post_field( 'post_author', $post_id ) ),
			),
		);
		if ( has_post_thumbnail( $post_id ) ) {
			$data['image'] = array( wp_get_attachment_image_url( get_post_thumbnail_id( $post_id ), 'large' ) );
		}
	} elseif ( is_front_page() ) {
		$data = array(
			'@context' => 'https://schema.org',
			'@type'    => 'WebSite',
			'name'     => get_bloginfo( 'name' ),
			'url'      => home_url( '/' ),
		);
	} else {
		return;
	}

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

WebSiteの構造化データは、Googleの検索結果に表示されるサイト名を決めるときの手がかりとして使われます。

なお、以前はWebSiteにSearchActionを追加して、検索結果にサイト内検索ボックスを表示させる方法がよく紹介されていました。この検索ボックスは2024年に提供が終了しているため、新しく追加する必要はありません。

参考:

パンくずリストの構造化データは、こちらの記事で紹介しています。

【Google・Yahoo】検索結果にパンくずリストを表示させる最も適切な方法【構造化データマークアップ】

7. 出力されているか確認する

コードを追加したら、次の方法で確認します。

確認するときは、トップページだけでなく、投稿ページ・カテゴリーページ・2ページ目以降も見ておくと安心です。キャッシュ系のプラグインを使っている場合は、キャッシュを削除してから確認しましょう。

まとめ

SEOプラグインを使わないWordPressサイトでは、個別ページは対応できていても、トップページや一覧ページの設定が漏れていることがよくあります。

今回のポイントをまとめると、次のとおりです。

  • WordPress本体のcanonicalは個別ページだけなので、一覧ページは自分で出力する
  • 2ページ目以降のcanonicalは、1ページ目ではなくそのページ自身にする
  • トップページのtitleとmeta descriptionは専用の文言にする
  • アイキャッチがないページ用に、1200×630の既定のOGP画像を用意する
  • 構造化データは、投稿ページにBlogPosting、トップページにWebSiteを出力する
  • SEOプラグインを使っている場合は、タグが二重にならないようにする

一度追加しておけば、記事を増やしても自動で出力されます。チェックツールなどで定期的に確認しながら、設定漏れがないか見直してみてください。

あわせて読みたい