同じサイトの/old配下を/new配下へ移し、後ろのパスとクエリ文字列を残すコードです。サイトのhome URLがドメイン直下の場合に限定します。サブディレクトリ設置やドメイン全体の移転にはそのまま使いません。
転送する範囲
/old、/old/、/old/about/?x=1を対象とし、/olderや/newは対象外です。旧サイトのWordPressまで到達するGET・HEADリクエストにだけ適用します。静的ファイル、管理画面、REST APIまで転送する仕組みではありません。
function waza_moved_path($uri) {
if (!is_string($uri) || preg_match('/[\x00-\x20\x7f\\\\]/', $uri)) return '';
$parts = wp_parse_url($uri);
if (!$parts || isset($parts['host']) || isset($parts['scheme']) || isset($parts['fragment'])) return '';
$path = $parts['path'] ?? '';
if ($path !== '/old' && strpos($path, '/old/') !== 0) return '';
// 特殊なエンコードや親ディレクトリ表記は自動変換しない。
if (preg_match('/%(?:2f|5c|2e|0[ad])/i', $path) || preg_match('#(?:^|/)\.{1,2}(?:/|$)#', $path)) return '';
$target = '/new' . substr($path, 4);
if (isset($parts['query'])) $target .= '?' . $parts['query'];
return $target;
}
add_action('template_redirect', function () {
if (is_admin() || is_feed() || is_preview() || is_trackback()) return;
if (!in_array($_SERVER['REQUEST_METHOD'] ?? '', array('GET','HEAD'), true)) return;
if (defined('REST_REQUEST') && REST_REQUEST) return;
$target = waza_moved_path(wp_unslash($_SERVER['REQUEST_URI'] ?? ''));
if ($target === '') return;
// 同じサイトの /old を /new へ移す例。検証中は302。
if (wp_safe_redirect(home_url($target), 302, 'WAZA path redirect')) exit;
}, 1);
設置と確認
旧側の子テーマfunctions.phpなどへ追加します。開始タグは既存PHP内には追加しません。/oldと/newは例なので、移転元と移転先の対応を確認してコードを変更します。転送先が存在すること、末尾スラッシュ、クエリ文字列の扱い、対象外URL、リダイレクトループがないことを確認してください。クエリに秘密情報が含まれる運用には流用しません。
検証中は302です。恒久移転と確認できた場合に限り302を301へ変更します。301はブラウザーや中間キャッシュに残るため、戻す際はコード停止だけでなくキャッシュの影響も確認します。SEO効果や順位維持を保証しません。
実際のリクエストで転送範囲を確認する
| リクエストの例 | このコードの結果 |
|---|---|
| GET /old/?x=1 | 302。/new/?x=1へ転送 |
| HEAD /old/about/ | 302。/new/about/へ転送 |
| POST /old/ | このコードでは転送しない |
| GET /older/・/new/ | このコードでは転送しない |
| GET /wordpress/old/ | この例の/old配下ではないため転送しない |
転送先のページが存在するかは、この関数だけでは確認しません。
旧URLの302とLocationを確認した後、新URLを別に開いて内容とHTTPステータスを確認します。
対象外URLは転送を行わず、通常のWordPress処理へ戻るため、200か404かは元のサイト構成で変わります。
プラグイン・Webサーバーとの使い分け
Redirectionは外部URLも指定できます。また、WordPressを使っていること自体が.htaccessの転送を無効にするわけではありません。サーバー種類やルール順序で結果が変わります。個別URLの管理はRedirectionの設定例も参照してください。
掲載コードはWordPress 7.1.2の隔離環境で、実際のGET・HEADの302応答とLocation、POSTと対象外パスの非転送を確認しました。
本番サイトの転送設定は追加していません。
サブディレクトリ設置への対応を確認した結果ではないため、冒頭の適用範囲を守ります。
戻す場合は今回の関数とフック登録を外します。
まとめと参考資料
特定のパス変更に伴う転送処理は、厳密なパス検証とwp_safe_redirectを組み合わせることで、オープンリダイレクト脆弱性を防ぎながら安全に実装できます。恒久移転時は動作確認後に301へ切り替えてください。
よくある質問:functions.phpで特定パスを転送する方法
このコードは、どんな転送をしますか?
記事では、同じサイトの/old配下を/new配下へ移し、後ろのパスとクエリ文字列を残すコードです。/old、/old/、/old/about/などを対象にし、/olderや/newは対象外です。
どんなサイトに使えますか?
記事では、サイトのhome URLがドメイン直下の場合に限定します。サブディレクトリ設置やドメイン全体の移転にはそのまま使いません。
どのリクエストが転送されますか?
旧サイトのWordPressまで到達するGET・HEADリクエストにだけ適用します。記事の表では、GET /old/やHEAD /old/about/は302で転送し、POST /old/、GET /older/、GET /new/、GET /wordpress/old/は転送しません。静的ファイル、管理画面、REST APIまで転送する仕組みではありません。
設置したあと、何を確認しますか?
旧側の子テーマのfunctions.phpなどへ追加します(既存PHP内には開始タグを追加しません)。記事では、転送先が存在すること、末尾スラッシュ、クエリ文字列の扱い、対象外URL、リダイレクトループがないことを確認してください。転送先のページが存在するかは、この関数だけでは確認しません。クエリに秘密情報が含まれる運用には流用しません。
302と301は、どう使い分けますか?
記事では、検証中は302です。恒久移転と確認できた場合に限り302を301へ変更します。301はブラウザーや中間キャッシュに残るため、戻す際はコード停止だけでなくキャッシュの影響も確認します。SEO効果や順位維持を保証しません。
プラグインやWebサーバーの設定とは、どう使い分けますか?
記事では、本稿のコードは旧サイトのWordPressまで到達する場合の例で、静的ファイルなどには適用されません。使い分けの詳細や、Redirectionの設定例は記事内で案内しています。
元に戻すには、どうしますか? 検証の範囲は?
戻す場合は今回の関数とフック登録を外します。記事では、GET・HEADの302応答とLocation、POSTと対象外パスの非転送を確認しており、本番サイトの転送設定は追加していません。サブディレクトリ設置への対応を確認した結果ではないため、冒頭の適用範囲を守ってください。WordPressの仕様は変わる可能性があるため、最新はWordPress公式のwp_safe_redirectの説明で確認してください。



