正規表現キャプチャ徹底解説:$1・%1・バックリファレンスと検証手順【Apache mod_rewrite対応】

正規表現キャプチャ徹底解説:$1・%1・バックリファレンスと検証手順【Apache mod_rewrite対応】 apache
本記事は「正規表現のキャプチャ(捕捉グループ)」を、Apache mod_rewrite を主題に体系化したものです。( )で囲んだ部分を後で使い回す仕組み(バックリファレンス)を、%1 / $1 の違い、非キャプチャ名前付きキャプチャ欲張り/最短マッチ検証方法まで含めて解説します。

用語の整理(まずここだけ読めばOK)

  • キャプチャ(捕捉グループ):正規表現で ( ... ) と丸カッコで囲んだ部分。マッチした文字列を保存し、後で参照できる。
  • バックリファレンス:保存した値を参照する記法。
    • $1, $2, ...RewriteRuleパターン側で作ったキャプチャを参照。
    • %1, %2, ...:直前の RewriteCond条件側)で作ったキャプチャを参照。

    $%参照元が違う点が最重要。

  • 非キャプチャグループ(?: ... )。グループ化はするが保存しない(番号を消費しない)。
  • 名前付きキャプチャ(?<name> ... )(PCRE)。Apacheの置換部では名前参照は不可番号で参照する(PHP/JSでは名前で参照可)。
  • 欲張り/最短+, *欲張り(できるだけ長く)+?, *?最短
  • アンカー^(先頭)、$(末尾)。範囲を固定し、過剰マッチを防ぐ。
  • 判定と実行RewriteCond判定RewriteRule実行。複数の条件は AND で評価され、すべて真なら直後の1本の RewriteRule が実行される。

mod_rewriteにおけるキャプチャの基本構文

<IfModule mod_rewrite.c>
  RewriteEngine On

  # (例)判定:ホスト名が "www." で始まるか?
  RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
  # 実行:%1(条件側で捕捉した非www部分)と同じパスへ301
  RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
</IfModule>
  • どこがキャプチャ? (.+) がキャプチャ。www. の後ろ全部を保存し、%1 で参照。
  • なぜ %1? 直前の RewriteCond のキャプチャ参照は %1RewriteRule のキャプチャ参照は $1
  • 注意^www\. のドットは正規表現では「任意1文字」なので \. とエスケープしないと誤マッチする。

「」と「%1」の違いを完全理解

# 例:サブドメインを %1 で取り出し、パスの数値IDを $1 で取り出す
RewriteEngine On

# 判定(条件側のキャプチャ):sub.example.com → %1 = "sub"
RewriteCond %{HTTP_HOST} ^([^.]+)\.example\.com$ [NC]

# 実行(ルール側のキャプチャ):/post/123 → $1 = "123"
RewriteRule ^post/([0-9]+)/?$ /app.php?sub=%1&id=$1 [QSA,L]
  • 条件側([^.]+)%1
  • ルール側([0-9]+)$1
  • 活用:条件でドメインや言語、ルールでパス断片を拾い、置換先で組み立て直すのが定石。

非キャプチャグループ (?: ... ) の使いどころ

# 例:"index.php/" の有無を許可して最短にする(番号を消費しない)
RewriteRule ^(?:index\.php/)?(.*)$ /index.php?path=$1 [QSA,L]
  • メリット:番号がズレないので、$1 の意味が変わらない。読みやすさと保守性が上がる。
  • 最短化? は直前の要素を「0回または1回」。(?: ... )? は「グループ全体が任意」。

名前付きキャプチャ(概念と注意)

# PCREの例(概念理解用)— 名前付きキャプチャ
(?<id>[0-9]+)   # "id" という名前で捕捉
# ただし mod_rewrite では置換部で "名前" 参照はできない(番号で参照する)
  • Apacheの置換では番号参照のみ$1 / %1 等。名前はPHPやJSなどの言語側で有効。
  • 実務Tip:mod_rewrite では「番号+コメント」で明示する(例:# $1 = postId)。

欲張り(Greedy)と最短(Lazy)で意味が変わる

# Greedy(できるだけ長く)
^(.+)/(.+)$

# Lazy(できるだけ短く)
^(.+?)/(.+)$
  • 違い:Greedy は後ろのグループが空になりやすい。Lazy は手前を最短で確定しやすい。
  • 実務:区切り文字(/.)の前で止めたい時は Lazy を検討。

アンカー(^/$)で範囲を固定する

# 末尾スラッシュ無しページのみを捕捉
^([A-Za-z0-9/_-]+)$
  • アンカー必須^$ を付けないと、意図しない位置でもマッチしがち。
  • エスケープ.? は意味を持つので \.\? に。

キャプチャ活用レシピ(行コメントで判定と実行を明示)

1) www除去:%1 の基本パターン

RewriteEngine On
# 判定:^www\.(.+)$ — www. の後ろ全部を (.+) で捕捉 → %1
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
# 実行:%1(非wwwホスト) + 同じパスへ 301
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]

2) 言語サブディレクトリを捕捉

# /ja/xxx → /index.php?lang=ja&path=xxx
RewriteRule ^(ja|en|fr)/(.*)$ /index.php?lang=$1&path=$2 [QSA,L]
  • $1 は言語コード、$2 は残りのパス。
  • 固定候補は (?: ... | ... ) で列挙。必要なら [A-Za-z]{2} など汎用化。

3) IDの抽出(数値のみ)

# /post/123/ → /post.php?id=123
RewriteRule ^post/([0-9]+)/?$ /post.php?id=$1 [QSA,L]

桁数制限は {1,6} などで与えると誤マッチが減る。

4) クエリ文字列から値を捕捉(条件側)

# 例:?ref=twitter を検知し内部でマーク
# 判定:QUERY_STRING に ref=... が含まれる → %1 = "twitter"
RewriteCond %{QUERY_STRING} (^|&)ref=([^&]+)(&|$)
# 実行:環境変数をセット(のちほどヘッダ等で可視化)
RewriteRule ^ - [E=REF:%2,L]
  • ポイント%2 は条件側キャプチャの2番目(refの値)。
  • クエリを正規化(不要パラメータを捨てる)するなら [QSD] を使う。

キャプチャが効いているかの確認手順

1) リダイレクトはヘッダで確認

curl -I http://example.com/post/123
# → HTTP/1.1 301 Moved Permanently
#    Location: https://example.com/post.php?id=123

2) 内部リライトは環境変数+一時ヘッダ

# 実行時に環境変数 HIT=1 を付与(ルール側での例)
RewriteRule ^post/([0-9]+)/?$ /post.php?id=$1 [E=HIT:1,L]
# ヘッダ出力(VHost/Directoryで)
Header set X-Rewrite "1" env=HIT

レスポンスに X-Rewrite: 1 が出れば、$1 を使うルールが発動したと分かる。調査後は削除。

3) 調査中のみ詳細ログ

LogLevel rewrite:trace2   # 終わったら warn に戻す

落とし穴とベストプラクティス

  • 「必要な所だけ」キャプチャ:グループ化だけなら (?: ... ) を使い、番号ズレを防ぐ。
  • 順序の影響:途中に ( ... ) を追加すると $2 → $3 のように番号がズレる。既存置換部の見直し必須。
  • アンカー徹底^/$ を使い、過剰マッチや意図しない部分一致を抑える。
  • ドットはエスケープ. は「任意1文字」。ドメインや拡張子では \. に。
  • MultiViews干渉:Apacheの Options -MultiViews を検討。拡張子隠し系と競合しやすい。
  • 名前付きキャプチャの過信NG:mod_rewriteの置換では番号参照のみ。コメントで意味を残す。

実装チェックリスト

  • [ ] 何を捕捉するかが明確(要件→正規表現→テストケース)
  • [ ] アンカーとエスケープを適切に使用
  • [ ] 不要箇所は (?: ... )(非キャプチャ)で番号保全
  • [ ] %1/$1 の参照元をコメントで明示
  • [ ] curl -I と一時ヘッダ/ログで実機検証



本記事は Apache mod_rewrite と PCRE を前提に記述しています。CDN/プロキシ配下では HTTPS 判定やクライアントIP取得方法が変わるため、前段のヘッダ(X-Forwarded-*)設計も合わせて確認してください。

コメント

タイトルとURLをコピーしました