WordPressに限らず、SEO対策に不可欠なXMLサイトマップ。
カスタマイズせずとも「Google XML Sitemaps」などのプラグインで瞬殺なわけですが、
多くのXMLサイトマッププラグインは、柔軟に設定できるように作られている分、
「このサイトでは要らない機能」がたくさん含まれることになるので、多少なりともリソースの無駄遣いになります。
独自実装する方法を覚えておけば、いろいろと応用も利くと思います。
XMLサイトマップを出力するカスタマイズ
まずは functions.php に、以下のように追記します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
add_filter( 'rewrite_rules_array', 'add_sitemap_rewrite_rules', 1, 1 ); add_action( 'do_feed_sitemap', 'load_sitemap_template' ); /** * FILTER HOOK : rewrite_rules_array * sitemap.xml へのアクセス時にURLをリライト */ public static function add_sitemap_rewrite_rules( $rules ) { $new_rules = array( '^sitemap\.xml$' => 'index.php?feed=sitemap', ); return array_merge( $new_rules, $rules ); } /** * ACTION HOOK : do_feed_sitemap * サイトマップ用のfeedテンプレートのロード */ function load_sitemap_template() { load_template( get_template_directory() . '/feed-sitemap.php'); } |
そして、テーマファイルに以下の内容で、feed-sitemap.php というファイルを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php header( 'Content-Type: text/xml; charset=' . get_option( 'blog_charset' ), true ); echo '<?xml version="1.0" encoding="' . get_option( 'blog_charset' ) . '"?>' . "\n"; ?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <url> <loc><?php echo home_url(); ?></loc> <lastmod><?php echo date( 'Y-m-d', strtotime( get_lastpostdate( 'blog' ) ) ); ?></lastmod> <changefreq>daily</changefreq> <priority>1.0</priority> </url> <?php while ( have_posts() ): the_post(); ?> <url> <loc><?php the_permalink(); ?></loc> <lastmod><?php the_modified_time( 'Y-m-d' ); ?></lastmod> <changefreq>weekly</changefreq> <priority>0.8</priority> </url> <?php endwhile; ?> </urlset> |
前提知識 WordPressのフィード
ご存知の方は読み飛ばしてください。
WordPressには、標準でXML形式等のRSSフィードを出力することができます。
https://{your-domain}/feed/
https://{your-domain}/feed/atom/
などで、各形式のフィードにアクセスできます。
普段WordPressのテーマをカスタマイズしている方でも、フィードが出力されるロジックはよくわかっていない方が多いのではないでしょうか。
まず、フィードのURLにアクセスした場合、WordPress標準のリライトルールに従って、
/feed/ → index.php?feed=feed
/feed/atom/ → index.php?feed=atom
などのようにリライトされ、クエリ変数 $feed に値がセットされます。
クエリ変数 $feed に値がセットされていると、do_feed_{$feed} というアクションが実行されます。
そして、このアクションの中で、load_template() が実行され、フィード用のテンプレートが読み込まれるわけです。
デフォルトで用意されている何種類かのフィードに対応するテンプレートは、全て /wp-includes/ の中に配置されていますが、
もちろん do_feed_{$feed} に独自の関数をフックして、テーマフォルダ内に置いた独自のフィードテンプレートを読み込ませることも可能です。
XMLサイトマップ出力の解説
では、さきほどのソースコードを見ていきましょう。
存在しない sitemap.xml にアクセスすると、当然のことながら404エラーになり、404テンプレートが表示されてしまいます。
そこで、rewrite_rules_array フィルタにフックして、リライトルール(WordPressのルーティングルール的なもの)を追加します。
専用に投稿タイプを作っても良いのですが、”sitemap” という名前のフィードにリライトしています。
言い換えると、クエリ変数 $feed に “sitemap” という値をセットしています。
“sitemap” という名前のフィードはデフォルトでは存在しないので、そのままだとエラーになります。
前述のとおり、この場合には、do_feed_sitemap というアクションが実行されますので、
このアクションに関数をフックして、テーマフォルダ内の feed-sitemap.php を読み込むようにします。
feed-sitemap.php では、投稿ループを用いて、各投稿のURLや更新日などをXMLの要素として出力していきます。
この例では、トップページと標準投稿の個別ページしか出力していません。
カテゴリアーカイブやカスタム投稿などを出力したければ、
get_posts() で各種投稿を取得してループする(または pre_get_posts アクションにフックして対象の投稿タイプを変える)、
テンプレート内で get_terms() によりタームの一覧を取得してループするなど、個別に対応していきます。
基本的には、各種ページ用のテンプレートと同じです。
XMLサイトマップを独自実装しようなどと考える皆さんなら朝飯前ですね。
サイトマップのURLの末尾にスラッシュがついてしまう問題
パーマリンク設定の末尾にスラッシュが入っている場合、
https://{your-domain}/sitemap.xml
にアクセスしようとしたら、
https://{your-domain}/sitemap.xml/
にリダイレクトされてしまいます。
サイトマップ用のテンプレートが表示されるにはされるのですが、これではあまりよろしくないですね。
パーマリンク設定末尾からスラッシュを取り除けば解決するのですが、全部のページのURLが、若干とはいえ変わってしまいます。
そこで、自動で末尾にスラッシュをつける(トレイリングスラッシュ)処理を、sitemap.xml に対してのみ除外します。
1 2 3 4 5 6 7 8 9 10 11 12 |
add_filter( 'redirect_canonical', 'remove_sitemap_trailingslash', 1, 2 ); /** * FILTER HOOK : redirect_canonical * sitemap.xml へのアクセス時に末尾にスラッシュをつけない */ function remove_sitemap_trailingslash( $redirect_url, $requested_url ) { if ( 'sitemap' == get_query_var( 'feed' ) ) { return $requested_url; } return $redirect_url; } |
redirect_canonical というフィルタに処理をフックしています。
見慣れないフィルタですが、WordPressがURLを正規化する処理の中に用意されているフィルタです。
サイトマップが重い問題
サイトマップは基本的にはサイトの全ページを出力するので、件数が多くなってくると、非常に重くなります。
ユーザがアクセスするわけではないので、多少レスポンスが遅いのは問題ないかもしれませんが、
URLと更新時刻くらいしか出力しないわけなので、わざわざ WP_Post オブジェクトや WP_Term オブジェクトを介さずに、
$wpdb を引っ張り出して、SQLで直接一覧データを取得・出力したほうが、いくらか早いでしょう。
ただし、そもそもXMLサイトマップには、1ファイルあたり5万URL、50MBまでという制限があります。
シンプルなXMLサイトマップなら、1URLあたり1KBもいかないと思いますが、
投稿のパーマリンクがタイトルそのまま長い日本語だったり、
画像検索対策で、XMLサイトマップに各ページの画像のURLをたくさん入れたりしていると、
意外とこの制限にあっさり到達してしまうかもしれません。
そんな場合には、年ごとや投稿タイプごとにサイトマップを分割し、sitemap.xml をサイトマップインデックスにすることになります。
XMLサイトマップを分割する
とりあえず勘所だけ書いておきます。
- query_vars フィルタを使ってクエリ変数を追加。例えば $sitemap 。
- リライトルールを変更し、sitemap-{$type} を動的にリライトする。そしてこの $type をクエリ変数 $sitemap に突っ込む。
- pre_get_posts アクションなりで、クエリ変数 $sitemap に応じて、投稿の取得条件を変える。
気が向いたらサンプルコード書きます。
・・・
あ、このブログのXMLサイトマップ、プラグイン使ってた。