2026年5月20日
2026年5月20日
WordPressでカスタムリライトルールを設定する方法【パーマリンク最適化】
はじめに
WordPressのリライトシステムは、ユーザーフレンドリーなURLを内部のクエリパラメータにマッピングする仕組みです。カスタムリライトルールを追加することで、/products/category/electronics/のような独自のURL構造を実現できます。本記事では、add_rewrite_rule()・add_rewrite_tag()の使い方から、デバッグ方法まで詳しく解説します。
症状・原因
- 独自のURL構造(例:
/team/yamada/や/events/2026/05/)でカスタムコンテンツを表示したい - プラグインで追加したカスタムエンドポイントが404エラーになる
- パーマリンク設定を変更しても新しいURLパターンが反映されない
解決手順
ステップ1:add_rewrite_rule()の構文と基本的な使い方
<?php
/**
* カスタムリライトルールの基本構文
* add_rewrite_rule( regex, redirect, priority )
*
* regex: マッチさせるURLの正規表現(サイトのルートからの相対パス)
* redirect: マッチした場合のリダイレクト先(内部クエリ文字列)
* priority: 'top'(WordPress標準より前)または 'bottom'(後)
*/
add_action( 'init', 'my_add_rewrite_rules' );
function my_add_rewrite_rules() {
// 例1:/team/{slug}/ → メンバープロフィールページ
// 正規表現のキャプチャグループ $matches[1] が redirect に使われる
add_rewrite_rule(
'^team/([a-z0-9-]+)/?$', // 正規表現
'index.php?pagename=team&member=$matches[1]', // リダイレクト
'top' // 優先度
);
// 例2:/events/{year}/{month}/ → 年月アーカイブ
add_rewrite_rule(
'^events/([0-9]{4})/([0-9]{2})/?$',
'index.php?post_type=event&event_year=$matches[1]&event_month=$matches[2]',
'top'
);
// 例3:/api/v1/{endpoint}/ → カスタムAPIエンドポイント
add_rewrite_rule(
'^api/v1/([a-z_-]+)/?$',
'index.php?my_api_endpoint=$matches[1]',
'top'
);
// 例4:末尾スラッシュあり・なし両方に対応する場合
add_rewrite_rule(
'^products/([a-z0-9-]+)/reviews/?$',
'index.php?post_type=product&name=$matches[1]&product_tab=reviews',
'top'
);
}
ステップ2:add_rewrite_tag()でカスタムクエリ変数を登録する
<?php
/**
* カスタムクエリ変数の登録
* add_rewrite_tag() で WordPress に認識させる
*
* 引数: add_rewrite_tag( tag, regex, query )
* tag: %変数名% 形式のタグ名
* regex: このタグが取りうる値の正規表現(バリデーション用)
* query: 対応するクエリ文字列パラメータ
*/
add_action( 'init', 'my_add_rewrite_tags' );
function my_add_rewrite_tags() {
// メンバースラッグ用のタグ
add_rewrite_tag( '%member%', '([a-z0-9-]+)' );
// イベント年月用のタグ
add_rewrite_tag( '%event_year%', '([0-9]{4})' );
add_rewrite_tag( '%event_month%', '([0-9]{2})' );
// カスタムAPIエンドポイント用のタグ
add_rewrite_tag( '%my_api_endpoint%', '([a-z_-]+)' );
// 商品タブ用のタグ(固定値のみ受け付ける)
add_rewrite_tag( '%product_tab%', '(reviews|specs|qa)' );
}
// add_rewrite_tag() の代わりに query_vars フィルターを使う方法(後述ステップ3)
// add_rewrite_tag() はパーマリンク構造にも使えるが、
// 単純にクエリ変数を認識させるだけなら query_vars フィルターで十分
ステップ3:query_varsフィルターでカスタム変数をホワイトリスト登録する
<?php
/**
* カスタムクエリ変数のホワイトリスト登録
* WordPressは認識していないクエリ変数を無視するため、
* query_vars フィルターで明示的に許可する必要がある
*/
add_filter( 'query_vars', 'my_register_query_vars' );
function my_register_query_vars( $vars ) {
// 許可するカスタムクエリ変数を配列に追加
$vars[] = 'member';
$vars[] = 'event_year';
$vars[] = 'event_month';
$vars[] = 'my_api_endpoint';
$vars[] = 'product_tab';
return $vars;
}
/**
* 登録したクエリ変数の値を取得する
* get_query_var() で取得できる
*/
function my_get_custom_query_var( $var, $default = '' ) {
$value = get_query_var( $var, $default );
// 値のバリデーション(必要に応じて)
if ( 'event_year' === $var && $value ) {
$year = absint( $value );
return ( $year >= 2000 && $year <= 2100 ) ? $year : $default;
}
return $value;
}
// テンプレート内での使用例
// $member_slug = get_query_var( 'member', '' );
// $event_year = get_query_var( 'event_year', date('Y') );
ステップ4:template_redirectでカスタムURLのテンプレートを処理する
<?php
/**
* カスタムURLにマッチした場合のテンプレート処理
* template_redirect フックを使う
*/
add_action( 'template_redirect', 'my_handle_custom_endpoints' );
function my_handle_custom_endpoints() {
// === メンバープロフィールページの処理 ===
$member = get_query_var( 'member', '' );
if ( $member ) {
// メンバーデータを取得(カスタムポストタイプやデータベースから)
$member_post = get_page_by_path( $member, OBJECT, 'team_member' );
if ( ! $member_post ) {
// メンバーが見つからない場合は404
global $wp_query;
$wp_query->set_404();
status_header( 404 );
nocache_headers();
include get_query_template( '404' );
exit;
}
// カスタムテンプレートを読み込む
$template = locate_template( array(
"team-member-{$member}.php",
'team-member.php',
'page.php',
'index.php',
) );
include $template;
exit;
}
// === カスタムAPIエンドポイントの処理 ===
$endpoint = get_query_var( 'my_api_endpoint', '' );
if ( $endpoint ) {
// APIレスポンスを返す
header( 'Content-Type: application/json; charset=utf-8' );
header( 'X-Content-Type-Options: nosniff' );
$allowed_endpoints = array( 'posts', 'categories', 'tags' );
if ( ! in_array( $endpoint, $allowed_endpoints, true ) ) {
status_header( 404 );
echo wp_json_encode( array( 'error' => 'Endpoint not found' ) );
exit;
}
$data = my_get_api_data( $endpoint );
echo wp_json_encode( $data );
exit;
}
}
/**
* カスタムAPIデータを返す
*/
function my_get_api_data( $endpoint ) {
switch ( $endpoint ) {
case 'posts':
$posts = get_posts( array( 'numberposts' => 10 ) );
return array_map( function( $post ) {
return array(
'id' => $post->ID,
'title' => get_the_title( $post ),
'url' => get_permalink( $post ),
);
}, $posts );
default:
return array();
}
}
ステップ5:flush_rewrite_rulesのタイミングとデバッグ
<?php
/**
* flush_rewrite_rules() のタイミング
* リライトルールを追加・変更した後は必ずフラッシュが必要
* ただし毎回のリクエストで実行するとパフォーマンスが大幅に低下する
*/
// 正しい実装:プラグインの有効化・無効化時のみフラッシュする
register_activation_hook( __FILE__, 'my_plugin_activate' );
register_deactivation_hook( __FILE__, 'my_plugin_deactivate' );
function my_plugin_activate() {
// まずリライトルールを登録
my_add_rewrite_rules();
my_add_rewrite_tags();
// 登録後にフラッシュ(有効化時のみ実行される)
flush_rewrite_rules();
}
function my_plugin_deactivate() {
// 無効化時にもフラッシュ(削除したルールを反映)
flush_rewrite_rules();
}
// 開発中にリライトルールをデバッグする方法
function my_debug_rewrite_rules() {
if ( ! current_user_can( 'manage_options' ) || ! isset( $_GET['debug_rewrite'] ) ) {
return;
}
global $wp_rewrite;
// 現在登録されているすべてのリライトルールを確認
$rules = $wp_rewrite->rules;
echo '<pre>';
echo "=== 登録済みリライトルール ===\n";
foreach ( (array) $rules as $regex => $redirect ) {
if ( strpos( $redirect, 'my_' ) !== false || strpos( $regex, 'team' ) !== false ) {
echo esc_html( $regex ) . "\n → " . esc_html( $redirect ) . "\n\n";
}
}
echo '</pre>';
exit;
}
add_action( 'init', 'my_debug_rewrite_rules' );
// パーマリンク設定画面で「変更を保存」すると flush_rewrite_rules() が実行される
// 開発中は管理画面の「設定 → パーマリンク」を保存して手動でフラッシュ可能
/**
* バージョン変更時に自動的にフラッシュする実装
*/
add_action( 'init', 'my_maybe_flush_rewrite_rules' );
function my_maybe_flush_rewrite_rules() {
$current_version = '1.0.0';
$saved_version = get_option( 'my_plugin_rewrite_version', '' );
if ( $current_version !== $saved_version ) {
my_add_rewrite_rules();
flush_rewrite_rules();
update_option( 'my_plugin_rewrite_version', $current_version );
}
}
注意事項
flush_rewrite_rules()をinitフックで毎回呼ばない:flush_rewrite_rules()は重い処理で、毎リクエストで実行するとデータベースへの余分なクエリが発生しパフォーマンスが大幅に低下します。有効化・無効化時のみ実行してください- 正規表現の
^と/?$: リライトルールの正規表現は^で始まり/?$または$で終わるパターンが基本です。サイトのルートからの相対パスにマッチします 'top'と'bottom'の使い分け:'top'はWordPressの標準ルールより優先され、'bottom'は後に追加されます。カスタムルールが標準ルールと競合する場合は'top'を使用してください- マルチサイトでの注意: マルチサイト環境ではリライトルールがネットワーク全体に影響する場合があります。サブサイトごとのルール追加には注意が必要です
まとめ
カスタムリライトルールの実装は、add_rewrite_rule()でURLパターンを定義し、add_rewrite_tag()またはquery_varsフィルターでクエリ変数を登録し、template_redirectでカスタム処理を行う3ステップで完成します。flush_rewrite_rules()はプラグインの有効化・無効化時のみ呼び出し、パフォーマンスへの影響を最小限に抑えることが重要です。関連記事:WordPressのWP_Queryを高度に活用する方法