Permalink Redirectの代替案!途中のパーマリンク変更のリダイレクト設定を考える

パーマリンク変更を途中で行った時に、さくっとリダイレクトが行えるPermalink Redirectプラグイン。
前から時々使っていて、配布サイトが更新停止された後も挙動に問題なさそうだったのであんまり代替案を考えてませんでした。

しかしこの間、Permalink Redirectプラグインが干渉してうまく動作しないという事象が発生。
それをきっかけに、そもそもどのような変更の時にどのようなリダイレクト設定が必要かというのを調べてみました。

この記事ではパーマリンク変更によるリダイレクトの必要有無の判断、およびPermalink Redirectプラグインの代替方法について検証してわかった内容をまとめてます。
同じようなお悩みの方の参考になれば幸いです!

6/1追記)Redirectionで一発で解決する機能が今後追加される予定!詳細を追記しました。

リダイレクト設定が必要な場合、不要な場合

WordPressは標準で持っているリダイレクトルールがあります。
パーマリンク構造を変更した時も、条件を満たしていると自動でリダイレクトが行われるので、全体的なリダイレクト作業が不要になる(※)場合も。
なので最初に条件を把握しておくのがおすすめです。

注意!

自動リダイレクトが行われる場合でも、スラッグが干渉する等でちゃんと自動リダイレクトが行われないケースがあります。
そのためパーマリンク変更後は、旧URLからちゃんとリダイレクトされるかチェックするようにしましょう
以前/%category%/%postname%/→/%postname%/に変更したとき、一部だけリダイレクト先が添付ファイルページになったり、スラッグを数字にしていた記事がリダイレクトしなかったりといった事象がありました_:(´ཀ`」 ∠):

パーマリンク変更に関する自動リダイレクトの可否に関しては、こちらの記事にわかりやすくまとめられてました。

WordPress パーマリンク設定やスラッグを変更したときのリダイレクトについて

ざっくり概要をまとめると、

● 変更前/変更後が“カスタム構造以外”同士の場合(基本、日付と投稿名、月と投稿名、数字ベース、投稿名の中)

  • 下記の場合は自動リダイレクトされる。
    • 変更前が”基本”の場合(変更後の構造問わず)
    • 変更後が”数字ベース”の場合(変更前の構造問わず)
  • 下記の場合は自動リダイレクトは行われない。
    • 変更後が”基本”の場合(変更前の構造問わず)
    • 変更前が”数字ベース”の場合(変更後の構造問わず)
  • それ以外は組み合わせによってまちまち

● 変更前/変更後がカスタム構造の場合

  • 下記の場合自動でリダイレクトされる
    /%category%/%postname%/→/%postname%/
    /%category%/%postname%/→/%postid%/

    *前者は子カテゴリがない場合という条件あり

という感じになっています。

これらのパターンに当てはまらない場合、自分でリダイレクト設定をする必要があります。

Permalink Redirectプラグインとは?

そもそもPermalink Redirectプラグインとはどういうものか。
一言でいうと、古いパーマリンクURLから新しいパーマリンクURLへのリダイレクト設定が簡単にできるプラグインです。

人気プラグインだったんですが、残念ながら現在は配布サイトが閉鎖されています。


「私はそれから成長しました、そして遊び場はもうありません。」
https://scott.yang.id.au/

いまは当時の最新版プラグインデータを紹介しているサイトがあるのでそこからDLして使えますが、自己責任でというところ。

Permalink Redirectの設定と使い方:パーマリンク構成を変更した時の301リダイレクト
Permalink redirect サイト廃止!パーマリンク変更はどうするか? | Kokoだけの話

このプラグインの何が便利だったかというと、1URLごとにリダイレクトルールを記述する必要なく古いパーマリンク構造を指定するだけで設定が完了すること


「Old Permalink Structures」に旧パーマリンク構造を入力

設定も管理もスマートでとても使いやすい!

代替案を考えるにあたって、似たような感じで簡潔な設定・管理で済む方法を考えてみました。

Permalink Redirectの代替案<場合別>

そのまんま同じ機能を持つプラグインは見つけられなかったので、まずは.htaccessに記述するコードベースで検証。
((記事後半に、.htaccessコードを簡単に設定できるWordPressプラグインを紹介

さきほどの記事で自動リダイレクト不可だった組み合わせの内、3パターンを除いてはざっくりながらローカルでリダイレクトできることを確認できました。

<自動リダイレクト不可の組み合わせ>

日付と投稿名→投稿名対応可能:方法(1)
月と投稿名→投稿名対応可能:方法(1)
数字ベース→基本対応可能:方法(2)
数字ベース→日付と投稿名対応可能:方法(2)
数字ベース→月と投稿名対応可能:方法(2)
数字ベース→投稿名対応可能:方法(1)
/%category%/%postname%/→/%category%/%post_id%/対応可能:方法(3)
/%category%/%post_id%/→/%category%/%postname%/対応可能:方法(2)
/%category%/%post_id%/→/%postname%/対応可能:方法(1)
/%postname%/→/%category%/%postname%/対応可能:方法(3)
/%postname%/→/%category%/%post_id%/対応可能:方法(3)
/%post_id%/→/%category%/%postname%/対応可能:方法(2)
/%post_id%/→/%category%/%post_id%/対応可能:方法(2)
日付と投稿名→基本不明
月と投稿名→基本不明
投稿名→基本不明

WordPress パーマリンク設定やスラッグを変更したときのリダイレクトについて

変更前/変更後のパーマリンクの組み合わせによって対応方法が異なってくるので、表の右列に下記方法の番号を記載してます。
ひとつひとつ紹介していきます。

(1)任意のパーマリンク構造から投稿名(/%postname%/)へのリダイレクト

有名なYoast SEOプラグイン制作元のYoastが提供してくれている、リダイレクトコード生成ツールを使います。


Create Redirects

このツールは新しいパーマリンク構造が/%postname%/の時のみ使用できます。
使い方は、自分のサイトのURL、サブディレクトリで動いているならそのディレクトリ、古いパーマリンク構造の選択して、[Generate Refirect]ボタンをクリックするだけ。

サンプルとして2パターンほど生成してみました。(注意書きは日本語訳)

1.数字ベース→/%postname%/の場合

RedirectMatch 301 ^/archives/(d+)$ https://yoast.com/?p=$1
*これは、投稿IDから投稿への2回目のリダイレクトをWordPressに依存していることに注意してください。

注釈の部分は、

  • 1回目のリダイレクト→/archives/123から/?p=123へリダイレクト(.htaccess)
  • 2回目のリダイレクト→/?p=123から現在のURLへのリダイレクト(Wprd{ressの自動リダイレクト。基本構造からは可能なため)

ということですね。

2./%category%/%postname%/→/%postname%/の場合

リダイレクトを行う必要はありません。WordPressがリダイレクトを行います。 ただし、カテゴリが少ない場合は、すべてのカテゴリにこのような行を.htaccessファイルの先頭に追加して、リダイレクトを高速化します。 <category>を各カテゴリのスラッグに置き換える必要があります。

RedirectMatch 301 ^/<category>/(.*)$ https://yoast.com/$1

こちらは自動リダイレクトが行われるため、リダイレクトの必要がないことを教えてくれます!
わかりやすい…優しい…

(2)/%postid%/系からのリダイレクト

旧パーマリンクにpostidが含まれている場合(数字ベース/archives/%postid%や/%category%/%post_id%/)に使える方法です。

上に書いた「数字ベース→/%postname%/」と同じように、

  1. 旧URLから正規表現でpostidを取り出し、基本構造(/?p=123)にリダイレクト
  2. wordpress内部自動リダイレクトで基本構造から新しいURLにリダイレクト

という形になります。

検証したところ、それぞれ以下の記述でリダイレクトを確認。

組み合わせリダイレクトルール
/%category%/%post_id%/ to /%postname%/
/%category%/%post_id%/ to /%post_id%/
/%category%/%post_id%/ to /%category%/%postname%/
ソースURL: ^/([^/]+)/(d+)/$
ターゲットURL: https://example.com/?p=$2
/%post_id%/→/%category%/%postname%/
/%post_id%/→/%category%/%post_id%/
ソースURL: ^/(d+)$
ターゲットURL: https://example.com/?p=$1
数字ベース→基本
数字ベース→日付と投稿名
数字ベース→月と投稿名
ソースURL ^/archives/(d+)/$
ターゲットURL https://example.com/?p=$1

(3)投稿名(/%postname%/)系からのリダイレクト

/%postname%/からのリダイレクトの場合、.htaccessでは記事情報をひっぱってこれないのでWordPressテンプレートでリダイレクトを行なっていきます。

/%postname%/→/%category%/%postname%/へのリダイレクトコードはこちら。
※ 紹介されていたコードに捕捉をいれています。

//テーマのfunctions.php
<?php # -*- coding: utf-8 -*-

// 404.phpの変わりにt5_redirect_to_category関数を呼び出す
add_filter( '404_template', 't5_redirect_to_category' );

function t5_redirect_to_category( $template )
{
	if ( ! is_404() )
		return $template;

	global $wp_rewrite, $wp_query;

	// 現在のパーマリンク構造が"/%category%/%postname%/"のときのみ処理を進める
	if ( '/%category%/%postname%/' !== $wp_rewrite->permalink_structure )
		return $template;

	// get_page_by_path()はページ名(スラッグ)を元にページオブジェクトを取得する
	// 投稿タイプ「post」に特定のスラッグ名($wp_query->query['category_name'])が存在する場合、ページオブジェクトを$postに返す
	if ( ! $post = get_page_by_path( $wp_query->query['category_name'], OBJECT, 'post' ) )
		return $template;

	// 投稿IDからページURLを取得
	$permalink = get_permalink( $post->ID );

	// ページURLへ301リダイレクトを行う
	wp_redirect( $permalink, 301 );
	exit;
}

Catch 404 after changing permalink structure from /%postname%/ to /%category%/%postname%/

上記コードの一部を変更すれば、以下の組み合わせでもリダイレクトできました。

/%postname%/→/%category%/%post_id%/
→15行目をif ( ‘/%category%/%post_id%/’ !== $wp_rewrite->permalink_structure )に変更

/%category%/%postname%/→/%category%/%post_id%/
→15行目をif ( “/%category%/%post_id%/” !== $wp_rewrite->permalink_structure )に変更
20行目をif ( ! $post = get_page_by_path( $wp_query->query_vars[‘category_name’], OBJECT, ‘post’ ) に変更

ちなみに、$wp_query->query[‘category_name’]が取得できないパーマリンクの場合だとこの方法は使えないみたいです。(/%postid%/や数字ベースからのリダイレクトだとNG)
パーマリンク構造によって取得できる情報に違いがあるから一筋縄ではいかない模様。

その他

調べている中で別の書き方もありました。何かのヒントになりそうだったものを紹介します。

(1)/%postname%/ → /%category%/%postname%/のリダイレクト

add_action( 'template_redirect', 'wpse_136306_redirect', 1 );
function wpse_136306_redirect() {
	global $wp;

	// Check that we've hit a 404 for a non-empty permalink without slashes
	if ( is_404() && strlen( $wp->request ) && strpos( $wp->request, '/' ) === false ) {
		// Attempt to find a post that matches the request.
		$posts = get_posts(
			array(
				'posts_per_page' => 1,
				'name' => $wp->request,
			)
		);

		if ( $posts && $url = get_permalink( $posts[0]->ID ) ) {
			// All good, let's redirect to the new URL.
			wp_redirect( $url, 301 );
			exit;
		}
	}
}

Redirect a “/%postname%/” permalink structure to the their new URL.

さっきのテンプレートとの違いは、パスからスラッグを切り出してwp_queryでひっぱってきているところです。

(2)/%postname%/%post_id → /%category%/%postname%/%post_id%のリダイレクト

add_action( 'parse_request', 'maybe_redirect_old_permalinks' );
function maybe_redirect_old_permalinks( $wp ) {
	if ( preg_match( '#^([^/]+)/(d+)$#', $wp->request, $matches ) ) {
		list ( $path, $slug, $id ) = $matches;
		// Redirect to the new permalink, if the slug and ID match.
		if ( $slug === get_post_field( 'post_name', $id ) ) {
			wp_redirect( get_permalink( $id ), 301 );
			exit;
		}
	}
}

Changing URL from /%postname%/%post_id to /%category%/%postname%/%post_id%

こちらは結構珍しいパーマリンクのタイプ。
正規表現と組み合わせればこんな感じで対応できるんだなぁと勉強になりました。

Redirectionプラグインでの設定方法

ここまで.htaccessに記述するコードをベースに紹介してきましたが、.htaccessはよくわからない、直接編集するのが怖いという人も多いと思います。

そんな人はRedirectionプラグインがおすすめ!
人気なので日本語の情報も多く、正規表現にも対応できます。


https://ja.wordpress.org/plugins/redirection/

さきほど紹介した正規表現のコードをRedirectionプラグインで設定する方法を紹介します。

まずRedirectionプラグインをインストールしたら、管理画面の左メニュー”ツール” → Redirectionをクリック。
転送ルールタブの画面下部にルールの追加エリアがあるので、キャプチャの通り設定していきます。

例えば数字ベース→/%postname%/の場合なら、
RedirectMatch 301 ^/archives/(d+)$ https://yoast.com/?p=$1
というコードなので、ソースURLに^/archives/(d+)$を、ターゲットURLにhttps://example.com/?p=$1を入力します。
(example.comドメインをご自身のドメインに置き換えてください)

URLオプション/正規表現はドロップダウンから正規表現を選択しましょう。

その後、[転送ルールを追加]をクリックすれば適用完了!
実際にリダイレクトするか、旧URLにアクセスして試してみてください。

ちなみに正規表現の検索対象を確認するとき、こちらのツールが便利でした。

正規表現チェッカー

Redirectionでパーマリンク指定だけでリダイレクトできる機能が追加予定<6/1追記>

Redirectionプラグインで、古いパーマリンク構造を指定するだけでリダイレクトしてくれる機能が追加される予定です。
すでにv5.0以降でベータ版の機能が試せるようになってますが、v5.1.1現在ベータ版で動作しないという報告が多々見られる状態。今のところ私のローカル環境でもうまく動きませんでした

今後、本機能として追加されるようになったらこの方法で全て解決しそうですね。

ベータ版は、[サイト]タブのPermalink Migrationという項目から試せます。
古いパーマリンク構造を入力し、[Add Permalink]をクリックすればOK。


Permalink Migration
「古いパーマリンク構造を入力して、現在のパーマリンク構造に自動的に移行します。
注: これはベータ版であり、投稿のみを移行します。」

今後が非常に楽しみです!

Site Options – Redirection

まとめ

注意!
  1. 上にも書いた通り、リダイレクト設定してもイレギュラーなページが出てくる場合があるので、パーマリンク変更後はなるべく全ページ旧URLからリダイレクトするか確認することをおすすめします。
  2. あくまでローカルで一部のURLで試した検証記録であることをご留意ください。

これまでWordPressのパーマリンク構造による仕様の違いや自動内部リダイレクトに関して深く考えることがなかったので、今回はいい機会になりました。
うかつにリダイレクト設定をしていくと簡単にごちゃりそうだなぁと改めて実感…

リダイレクトが必要になったときは設定が必要かどうかを確認して、なるべくシンプル&管理し易いよう留意して作業していきましょう!