mod_rewrite でクエリ文字列を一部だけ置き換えてリダイレクト

mode_rewrite でのリダイレクト設定に苦戦したのでメモ。
URLに複数のクエリ文字列がくっついていて、その中の特定のパラメータだけ置換して後は引き継ぎたい、という設定。

前提

- http/https のどちらであるかは場合による。

- jsessionid がついているかどうかは場合による。
ついていれば引き継ぐ必要がある。

- fuck=suck というパラメータがついている場合、
suck=fuck に置き換えてリダイレクトしたい。

- 他にも色々なパラメータがついてきたりこなかったりするけど
上記以外は置換せずに引き継いでリダイレクトしたい。

- クエリ文字列の順序は不定

解法

httpd.conf に以下の設定を行う:

# mod_rewrite をロードする
LoadModule rewrite_module modules/mod_rewrite.so

# url の rewrite を有効にする
RewriteEngine on

# クエリストリング(?の後)の条件を正規表現で指定する
#
# ^                                     # クエリ文字列の開始位置
# ((?:&?\w+=\w+)*)                      # 存在する可能性のあるパラメータ(1)
#                                       # 先頭の場合は & が付かない
# (?:&?fuck=suck)                       # 置換対象のクエリ文字列
# ((?:&\w+=\w+)*)                       # 存在する可能性のあるパラメータ(2)
# $                                     # クエリ文字列の終了位置
RewriteCond %{QUERY_STRING} ^((?:&?\w+=\w+)*)(?:&?fuck=suck)((?:&\w+=\w+)*)$

# リクエストパス(?の前)の条件と置換するURL文字列を正規表現で指定する
# ^/hoge\.do(;jsessionid=.*)?$          # 置換元リクエストパス
# /hoge.do$1?%1&suck=fuck%2             # 置換先リダイレクトパス
#                                       # $1:jsessionid
#                                       # %1:RewriteCondでマッチしたクエリ文字列前半
#                                       # %2:同上、クエリ文字列後半
# [R=301,L]                             # 処理フラグ
#                                       # R=301 リダイレクトコード (permanent moved)
#                                       # L     置換処理終了フラグ
RewriteRule ^/hoge\.do(;jsessionid=.*)?$ /hoge.do$1?%1&suck=fuck%2 [R=301,L]

これでリダイレクトはできるんだけど、jsessionidが付いている場合に区切り文字のセミコロンが%3bに化けてしまう。tomcat上ではセッション認識しているので動作上は問題ないけど何か気持ち悪い...。