• 最終更新日:

WordPressでSEOを意識したhead内の書き方とは? - OGP設定を含む

WordPressでSEOを意識したhead内の書き方

今回はWordPressでSEOを意識したhead内の書き方について説明します。

前提条件は以下

  • プラグインは使いたくない
  • 独自でheadの中をカスタマイズしたい

説明環境は以下

  • macOS Monterey 12.5
  • Visual Studio Code v1.70.1
  • WordPress v6.0
この記事の目次

WordPressでSEO対策を意識した書き方

SEOを意識したhead内の情報は以下になります。
これをWordPressでページごとに出力する設定を説明していきます。

<!-- schema.orgで構造化マークアップ -->
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#">
<!-- 文字エンコーディング情報を出力 -->
<meta charset="UTF-8" />
<!-- 電話番号の自動リンク機能を無効化 --> 
<meta name="format-detection" content="telephone=no" /> 
<!-- レスポンシブ対応 -->
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<!-- IEでも常に標準モードで表示 -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>サイトのタイトル</title>
<meta name="description" content="サイトの説明分文" />
<link rel="canonical" href="ページのURL">
<!-- 検索エンジンに読み込ませるか -->
<meta name="robots" content="index,follow">
<!-- OGP設定 -->
<meta property="og:type" content="website">
<meta property="og:title" content="ページのタイトル">
<meta property="og:description" content="ページの説明文">
<meta property="og:url" content="ページのURL">
<meta property="og:image" content="ページのメイン画像">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:site_name" content="サイトの名前">
<meta property="og:locale" content="ja_JP" />
<meta property="fb:app_id" content="facebookのID">
<!-- Twitter設定 -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:creator" content="twitterのID">
<meta name="twitter:site" content="twitterのID">
</head>

用意するファイル

すべてをheader.phpに書かず、編集しやすいようにファイル分割して管理します。

  • header.php … 共通の設定とseo.phpとogp.phpの読み込み用
  • ogp.php … 各ページごとのogp設定
  • seo.php … 各ページごとのogp設定以外(titleやdescriptionなど)
|_Theme/
   |- header.php
   |- seo.php
   |- ogp.php

header.phpで書くこと

header.phpで書く内容は以下になります。
3〜7行目でogpを使うための宣言をしています。
11行目と12行目でseo.phpとogp.phpを読み込んでいます。

<!DOCTYPE html>
<html lang="ja">
<?php if ( is_single() ) : ?>
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article#">
<?php else : ?>
<head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# website: http://ogp.me/ns/website#">
<?php endif; ?>
	<meta charset="UTF-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
	<meta name="format-detection" content="telephone=no" />
	<?php get_template_part( 'seo' );// SEOの設定テンプレート! ?>
	<?php get_template_part( 'ogp' ); // OGPの設定テンプレート! ?>
	<?php wp_head(); ?>
</head>

seo.phpで書くこと

seo.phpでは以下の4つを各ページごとに出力しています。

  • title … ページのタイトル
  • description … ページの説明文
  • canonical … canonicalタグの設定
  • robots … index,followの設定
<?php
// function.phpで記述されている定義のものにnoindexをつける
if ( is_noindex_page() ) :
	?>
<meta name="robots" content="noindex,follow">
<?php endif; ?>
<!--(1)トップページ-->
<?php if ( is_home() || is_front_page() ) : ?>
	<title><?php wp_title( '|', true, 'right' ); ?><?php bloginfo( 'name' ); ?></title>
	<meta name="description" content="<?php bloginfo( 'description' ); ?>" />
<!--(2)single.php-->
<?php elseif ( is_single() ) : ?>
	<title><?php wp_title( '|', true, 'right' ); ?><?php bloginfo( 'name' ); ?></title>
	<?php if ( has_excerpt( $post->ID ) ) : ?>
	<meta name="description" content="<?php echo esc_html( get_the_excerpt() ); ?>" />
	<?php else : ?>
		<?php if ( have_posts() ) : ?>
			<?php
			while ( have_posts() ) :
				the_post();
				?>
				<?php
				$des  = get_the_content();
				$des  = strip_tags( $des );
				$des  = str_replace( ' ', ' ', $des );// 改行を除去!
				$des  = str_replace( array( "\r\n", "\r", "\n" ), '', $des );// 余計な文字列を除去!
				$desp = mb_substr( $des, 0, 120, 'UTF-8' );
				if ( $desp ) {
					echo '<meta name="description" content="' . esc_html( $desp ) . '" />';
				} else {
					echo '<meta name="description" content="' . esc_html( get_bloginfo( 'description' ) ) . '" />';
				}
				?>
			<?php endwhile; ?>
			<?php endif; ?>
	<?php endif; ?>
<!--(3)投稿タイプ アーカイブ -->
<?php elseif ( is_post_type_archive() ) : ?>
	<title><?php wp_title( '|', true, 'right' ); ?><?php bloginfo( 'name' ); ?></title>
	<?php
		$custon_description = get_post_type_object( get_post_type() )->description;
		$custon_description = str_replace( array( "\r\n", "\r", "\n" ), '', $custon_description );
	if ( $custon_description ) {
		echo '<meta name="description" content="' . esc_html( $custon_description ) . '" />';
	} else {
		echo '<meta name="description" content="' . esc_html( get_bloginfo( 'description' ) ) . '" />';
	};
	?>
<!--(3)タクソノミー アーカイブ -->
<?php elseif ( is_tax() ) : ?>
	<title><?php single_term_title( '', true ); ?> | <?php bloginfo( 'name' ); ?></title>
	<?php
		$custon_description = strip_tags( term_description() );
		$custon_description = str_replace( array( "\r\n", "\r", "\n" ), '', $custon_description );
	if ( $custon_description ) {
		echo '<meta name="description" content="' . esc_html( $custon_description ) . '" />';
	} else {
		echo '<meta name="description" content="' . esc_html( get_bloginfo( 'description' ) ) . '" />';
	};
	?>
<!--(4)カテゴリー -->
<?php elseif ( is_category() ) : ?>
	<?php if ( ! is_paged() ) : ?>
		<title><?php single_cat_title( '', true ); ?> | <?php bloginfo( 'name' ); ?></title>
		<?php
			$cat_id  = get_queried_object()->cat_ID;
			$post_id = 'category_' . $cat_id;
			$text    = category_description();
			$text    = strip_tags( $text );
			$text    = mb_substr( $text, 0, 120, 'UTF-8' );
		if ( $text ) {
			echo '<meta name="description" content="' . esc_html( $text ) . '" />';
		}
			echo '<meta name="description" content="' . esc_html( get_bloginfo( 'description' ) ) . '" />';
		?>
	<?php else : ?>
		<title><?php show_page_number( '' ); ?>ページ目 <?php single_cat_title( '', true ); ?> | <?php bloginfo( 'name' ); ?></title>
	<?php endif; ?>
<!--(5)固定ページ -->
<?php elseif ( is_page() ) : ?>
		<title><?php wp_title( '|', true, 'right' ); ?><?php bloginfo( 'name' ); ?></title>
		<?php if ( has_excerpt( $post->ID ) ) : ?>
	<meta name="description" content="<?php echo esc_html( get_the_excerpt() ); ?>" />
	<?php else : ?>
		<?php if ( have_posts() ) : ?>
			<?php
			while ( have_posts() ) :
				the_post();
				?>
				<?php
					$des  = get_the_content();
					$des  = strip_tags( $des );
					$des  = str_replace( ' ', ' ', $des );// 改行を除去!
					$des  = str_replace( array( "\r\n", "\r", "\n" ), '', $des );// 余計な文字列を除去!
					$desp = mb_substr( $des, 0, 120, 'UTF-8' );
				if ( $desp ) {
					echo '<meta name="description" content="' . esc_html( $desp ) . '" />';
				} else {
					echo '<meta name="description" content="' . esc_html( get_bloginfo( 'description' ) ) . '" />';
				}
				?>
		<?php endwhile; ?>
	<?php endif; ?>
	<?php endif; ?>
	<!--(6)検索結果ページ -->
<?php elseif ( is_search() ) : ?>
	<title>検索結果 | <?php bloginfo( 'name' ); ?></title>
<!--(7)404ページ -->
<?php elseif ( is_404() ) : ?>
	<title>お探しのページはございません | <?php bloginfo( 'name' ); ?></title>
<!--(8)その他 -->
<?php else : ?>
	<title><?php wp_title( '|', true, 'right' ); ?><?php bloginfo( 'name' ); ?></title>
	<meta name="description" content="<?php bloginfo( 'description' ); ?>" />
<?php endif; ?>
<!-- canonicalの設定 -->
<?php
if ( ! is_404() && ! is_search() ) { // 404ページと検索ページでなければ表示!
	echo '<link rel="canonical" href="https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . '">';
}?>

上記のseo.phpに書いたコードだけでは正しく機能しません
正しく機能させるためにはfunctions.phpに以下のコードを加える必要があります。

追加でfunctons.phpに書くコード

検索エンジンが1つのサイトで登録できるページ数には限りがあります。余計なページをインデックスさせないようにfunctions.phpに以下を追加しましょう。

以下のページは検索エンジンからインデックスさせないようにします。

  • 月のアーカイブ
  • 日のアーカイブ
  • タグページアーカイブ
  • 検索結果ページ
  • 404ページ
  • 分割されたページ
  • 添付ファイルアーカイブ
  • 作成者アーカイブ
/**
 * Indexしないページの定義
 */
if ( ! function_exists( 'is_noindex_page' ) ) :
	function is_noindex_page() {
		return ( is_month() ) || // 月のアーカイブページはインデックスに含めない!
		is_date() || // 日のアーカイブはインデックスに含めない!
		is_tag() || // タグのアーカイブページをインデックスに含めない!
		is_search() || // 検索結果ページはインデックスに含めない!
		is_404() || // 404ページはインデックスに含めない!
		is_paged() || //分割されたページはインデックスに含めない!
		is_attachment() || //添付ファイルページはインデックスに含めない!
		is_author(); //作成者のアーカイブページはインデックスに含めない!
	}
endif;

また分割したページを判定するために、以下もfunctions.phpに加えてます。

// 何ページ目か判定する定義
function show_page_number() {
	global $wp_query;
	$paged    = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
	$max_page = $wp_query->max_num_pages;
	echo $paged;
}

WordPresssでは自動でcanonicalタグを出力されるので、それを止めるために以下をfunctions.phpに加えます。

// WordPress独自のcanoicalタグ出力を停止
remove_action('wp_head', 'rel_canonical');

固定ページでも「抜粋」機能が使えるように以下をfunctions.phpに加えます。

// 固定ページで抜粋を入力できるようにする
add_post_type_support( 'page', 'excerpt' );

ogp.phpで書くこと

ogp.phpで設定するのは以下です。fb:app_idについては、特に設定しなくても機能するのでcontentの中は空欄で問題ありません。

  • og:type
  • og:title
  • og:description
  • og:url
  • og:image
  • og:image:width
  • og:image:height
  • og:site_name
  • og:locale
  • fb:app_id
  • twitter:card
  • twitter:creator
  • twitter:site
<!-- OGP -->
<!-- og:type -->
<meta property="og:type" content="<?php echo ( is_single() ? 'article' : 'website' ); ?>">
<!--(1)トップページ -->
<?php if ( is_home() || is_front_page() ) : ?>
	<meta property="og:title" content="<?php wp_title( '|', true, 'right' ); ?><?php bloginfo( 'name' ); ?>" />
	<meta property="og:description" content="<?php bloginfo( 'description' ); ?>" />
<!--(2)single.php -->
<?php elseif ( is_single() ) : ?>
	<meta property="og:title" content="<?php the_title(); ?>" />
	<?php if ( has_excerpt( $post->ID ) ) : ?>
	<meta property="og:description" content="<?php echo esc_html( get_the_excerpt() ); ?>" />
	<?php else : ?>
			<?php if ( have_posts() ) : ?>
				<?php
				while ( have_posts() ) :
					the_post();
					?>
					<?php
						$des  = get_the_content();
						$des  = strip_tags( $des );
						$des  = str_replace( ' ', ' ', $des );// 改行を除去!
						$des  = str_replace( array( "\r\n", "\r", "\n" ), '', $des );// 余計な文字列を除去!
						$desp = mb_substr( $des, 0, 120, 'UTF-8' );
					if ( $desp ) {
						echo '<meta property="og:description" content="' . esc_html( $desp ) . '" />';
					} else {
						echo '<meta property="og:description" content="' . esc_html( get_bloginfo( 'description' ) ) . '" />';
					}
					?>
				<?php endwhile; ?>
			<?php endif; ?>
	<?php endif; ?>
<!--(3)投稿タイプアーカイブ -->
<?php elseif ( is_post_type_archive() ) : ?>
	<meta property="og:title" content="<?php wp_title( '|', true, 'right' ); ?><?php bloginfo( 'name' ); ?>" />
	<?php
		$custon_description = get_post_type_object( get_post_type() )->description;
		$custon_description = str_replace( array( "\r\n", "\r", "\n" ), '', $custon_description );
	if ( $custon_description ) {
		echo '<meta property="og:description" content="' . esc_html( $custon_description ) . '" />';
	} else {
		echo '<meta property="og:description" content="' . esc_html( get_bloginfo( 'description' ) ) . '" />';
	};
	?>
<!--(4)タクソノミー アーカイブ -->
<?php elseif ( is_tax() ) : ?>
	<meta property="og:title" content="<?php single_term_title( '', true ); ?> | <?php bloginfo( 'name' ); ?>" />
	<?php
		$custon_description = strip_tags( term_description() );
		$custon_description = str_replace( array( "\r\n", "\r", "\n" ), '', $custon_description );
	if ( $custon_description ) {
		echo '<meta property="og:description" content="' . esc_html( $custon_description ) . '" />';
	} else {
		echo '<meta property="og:description" content="' . esc_html( get_bloginfo( 'description' ) ) . '" />';
	};
	?>
<!--(5)category.php -->
<?php elseif ( is_category() ) : ?>
	<?php if ( ! is_paged() ) : ?>
		<meta property="og:title" content="<?php single_cat_title( '', true ); ?> | <?php bloginfo( 'name' ); ?>" />
		<?php
			$cat_id  = get_queried_object()->cat_ID;
			$post_id = 'category_' . $cat_id;
			$text    = category_description();
			$text    = strip_tags( $text );
			$text    = mb_substr( $text, 0, 120, 'UTF-8' );
		if ( $text ) {
			echo '<meta property="og:description" content="' . esc_html( $text ) . '" />';
		} else {
			echo '<meta property="og:description" content="' . esc_html( get_bloginfo( 'description' ) ) . '" />';
		}
		?>
	<?php else : ?>
		<meta property="og:title" content="<?php show_page_number( '' ); ?>ページ目 <?php single_cat_title( '', true ); ?>" />
	<?php endif; ?>
<!--(6)固定ページ -->
<?php elseif ( is_page() ) : ?>
	<meta property="og:title" content="<?php the_title(); ?> | <?php bloginfo( 'name' ); ?>" />
		<?php if ( has_excerpt( $post->ID ) ) : ?>
		<meta property="og:description" content="<?php echo esc_html( get_the_excerpt() ); ?>" />
	<?php else : ?>
		<?php if ( have_posts() ) : ?>
			<?php
			while ( have_posts() ) :
				the_post();
				?>
				<?php
					$des  = get_the_content();
					$des  = strip_tags( $des );
					$des  = str_replace( ' ', ' ', $des );// 改行を除去!
					$des  = str_replace( array( "\r\n", "\r", "\n" ), '', $des );// 余計な文字列を除去!
					$desp = mb_substr( $des, 0, 120, 'UTF-8' );
				if ( $desp ) {
					echo '<meta property="og:description" content="' . esc_html( $desp ) . '" />';
				} else {
					echo '<meta property="og:description" content="' . esc_html( get_bloginfo( 'description' ) ) . '" />';
				}
				?>
		<?php endwhile; ?>
	<?php endif; ?>
	<?php endif; ?>
<!--(7)その他 -->
<?php else : ?>
	<meta property="og:title" content="<?php bloginfo( 'name' ); ?>" />
	<meta property="og:description" content="<?php bloginfo( 'description' ); ?>" />
<?php endif; ?>
<!-- og:urlの設定 -->
<?php
if ( ! is_404() && ! is_search() ) { // 404ページと検索ページでなければ表示!
	echo '<meta property="og:url" content="https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . '">';
  echo "\n";
}
?>
<!-- og:imageの設定 -->
<?php
$str            = $post->post_content;
$search_pattern = '/<img.*?src=(["\'])(.+?)\1.*?>/i'; // 投稿にイメージがあるか調べる!
if ( is_single() ) {// 単一記事ページの場合!
	if ( has_post_thumbnail() ) {// 投稿にサムネイルがある場合の処理!
		$image_id = get_post_thumbnail_id();
		$image    = wp_get_attachment_image_src( $image_id, 'full' );
		echo '<meta property="og:image" content="' . esc_url( $image[0] ) . '">';
		echo "\n";
		echo '<meta property="og:image:width" content="' . $image[1] . '">';
		echo "\n";
		echo '<meta property="og:image:height" content="' . $image[2] . '">';
		echo "\n";
	} elseif ( preg_match( $search_pattern, $str, $imgurl ) && ! is_archive() ) {// 投稿にサムネイルは無いが画像がある場合の処理
		echo '<meta property="og:image" content="' . esc_url( $imgurl[2] ) . '">';
		echo "\n";
		echo '<meta property="og:image:width" content="1200">';
		echo "\n";
		echo '<meta property="og:image:height" content="630">';
		echo "\n";
	} else { // 投稿にサムネイルも画像も無い場合の処理!
		$ogp_image = get_template_directory_uri() . '/images/og-image.jpg';
		echo '<meta property="og:image" content="' . esc_url( $ogp_image ) . '">';
		echo "\n";
		echo '<meta property="og:image:width" content="1200">';
		echo "\n";
		echo '<meta property="og:image:height" content="630">';
		echo "\n";
	}
} else { // 単一記事ページページ以外の場合(アーカイブページやホームなど)!
	if ( get_header_image() ) {// ヘッダーイメージがある場合は、ヘッダーイメージを!
		echo '<meta property="og:image" content="' . esc_url( get_header_image() ) . '">';
		echo "\n";
		echo '<meta property="og:image:width" content="1200">';
		echo "\n";
		echo '<meta property="og:image:height" content="630">';
		echo "\n";
	} else { // ヘッダーイメージがない場合は、テーマのスクリーンショット!
		echo '<meta property="og:image" content="' . esc_url( get_template_directory_uri() ) . '/screenshot.png">';
		echo "\n";
		echo '<meta property="og:image:width" content="1200">';
		echo "\n";
		echo '<meta property="og:image:height" content="630">';
		echo "\n";
	}
}
?>
<!-- og:その他 -->
<meta property="og:site_name" content="<?php bloginfo( 'name' ); ?>">
<meta property="og:locale" content="ja_JP" />
<meta property="fb:app_id" content="">
<!-- Twitter設定 -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:creator" content="twitterのID">
<meta name="twitter:site" content="twitterのID">

設定する際の注意点

サイトの基本情報は【 設定 > 一般 】から設定しておきましょう。
このサイトは以下のように設定しています。

投稿記事と個別記事については、「抜粋」にテキストがあれば優先されてdescriptionに設定され、「抜粋」に何も無ければ本文の120文字がdescriptionに設定されます。

カスタム投稿タイプのアーカイブについて、descriptionを設定するためには、カスタム投稿タイプを作成したときに、一緒にdescriptionを書いておく必要があります。何も設定されていない場合はサイトのキャッチフレーズがdescriptionに反映されます。

以下はカスタム投稿タイプがnewsで作成した例です。17行目でdescriptionを設定しています。

function news_init() {
	$labels = array(
		'name'               => _x( 'News', 'post type general name' ),
		'singular_name'      => _x( 'News', 'post type singular name' ),
		'add_new'            => _x( '新規追加', 'News' ),
		'add_new_item'       => __( '新しくNewsを追加する' ),
		'edit_item'          => __( 'Newsを編集' ),
		'new_item'           => __( '新しいNews' ),
		'view_item'          => __( 'Newsを見る' ),
		'search_items'       => __( 'Newsを探す' ),
		'not_found'          => __( 'Newsはありません' ),
		'not_found_in_trash' => __( 'ゴミ箱にNewsはありません' ),
		'parent_item_colon'  => '',
	);
	$args   = array(
		'labels'             => $labels,
		'description'        => 'Support-Rの更新情報一覧ページです。',
		'public'             => true,
		'publicly_queryable' => true,
		'show_ui'            => true,
		'query_var'          => true,
		'rewrite_withfront'  => true,
		'rewrite'            => true,
		'capability_type'    => 'post',
		'hierarchical'       => false,
		'menu_position'      => 5,
		'menu_icon'          => 'dashicons-edit-large',
		'show_in_rest'       => false, /* カスタム投稿でgutenberg有効化する場合はtrue/false*/
		'supports'           => array( 'title', 'editor', 'page-attributes' ),
		'has_archive'        => true,

	);
	register_post_type( 'news', $args );
}
add_action( 'init', 'news_init' );

カテゴリーやタームの説明文を加えておけばそれぞれのアーカイブページで反映されます。

ogpの画像について、投稿記事にサムネイル画像が設定されていれば、その画像が設定され、サムネイル画像がない場合は投稿記事内にある最初の画像を取得して、その画像が設定されます。

それでも画像がない場合はimagesフォルダにあるog-image.jpgが表示される設定です。og-image.jpgはご自身で用意してもらい、サイズは1200 x 630にしておきます。

投稿記事以外ではheader画像が設定されていれば、その画像がogp画像として設定され、header画像がない場合はscreenshot.pngがogp画像になります。

まとめ

今回はWordpressでSEOを意識したhead内の書き方について説明しました。プラグインに頼らず、独自に設定したい方は参考にしてみてください。

プログラミングの知識がない場合は、SEO SIMPLE PACKというプラグインを使えば簡単に設定することができます。説明も日本語で、管理画面上から設定できるのでオススメです。