« apacheのフィルタ機能 | メイン | Israel »

apacheのrewriteモジュールとproxyモジュールを同時に使う

apacheでは、proxyモジュールとrewriteモジュールを使って、proxyリクエストのURIを書き換えることができます。ただ、その設定はちょっと分かりづらくて、しかもapacheのバージョンによって設定の仕方が異なります。今回はそのやり方をまとめてみました。

以下のようなケースを考えます。

ブラウザ --- プロキシ(apache) --- Webサーバ

プロキシサーバのホスト名はsvr1
Webサーバのホスト名はsvr2
であるとします。ブラウザはプロキシsvr1をつかってsvr2にアクセスすることとします。

このとき、
http://svr2/foo/ へのリクエストは、実際にはsvr2には送らずに、svr1が横取りをして、http://svr1/bar/ の内容を返すようにするにはどのような設定をしたらよいでしょう。

apacheでは、URLの書き換えにはRewriteRule ディレクティブを使います。
これは、第一引数にはマッチさせるパターン、第二引数には置き換えるテキストを指定します。
プロキシリクエストをマッチさせるためには、パターンのところに、

^proxy:http://svr2/foo/(.*)$

のような正規表現を書きます。proxy: があるところと、サーバ名があるところが通常のHTTPリクエストと違います。
以上で設定終わり♪と言えればよいのですが、そう簡単にはいきません。

・モジュールをロードする順番
・RewriteRuleの第二引数側にproxyのホスト名をつけるか
・RewriteRuleの第三引数の、[P]の有無
が、動作に影響を与えるようです。

結論としては、1.3系、2.0系、2.2系それぞれでうまく行くやり方はあるのですが、そのやり方はバージョンごとに違います。以下に正常動作する設定をまとめました。

[1.3系]
LoadModule proxy_module libexec/libproxy.so
LoadModule rewrite_module libexec/mod_rewrite.so
RewriteRule ^proxy:http://svr2/foo/(.*)$ /bar/$1
[P]の有無は関係なし

[2.0系]
LoadModule rewrite_module libexec/mod_rewrite.so
LoadModule proxy_module libexec/libproxy.so
RewriteRule ^proxy:http://svr2/foo/(.*)$ http://svr1/bar/$1
[P]の有無は関係なし

   または

RewriteRule ^proxy:http://svr2/foo/(.*)$ /bar/$1
[P]があるとだめ

[2.2系]
RewriteRule ^proxy:http://svr2/foo/(.*)$ http://svr1/bar/$1
モジュールの順番は関係なし
[P]の有無は関係なし

2.2が一番くせがないです。
1.3と2.0でモジュールをロードする順番が逆なので、移行の時などにははまりやすそうです。
また、DSOを使わずにモジュールをコンパイルインしている場合は、proxy, rewriteの順に読み込まれるようなので、2.0だと動かないということになります。

ちなみに、間違った組み合わせで設定してしまうと、以下のような現象が起きます。
[1.3系]
モジュールロードの順番を間違えたり、RewriteRuleの第二引数にhttp://svr1/を付け加えてしまうと、
無限ループ(MaxClientsに達するまでapacheが内部で同じリクエストを発行し続ける)になります。
[L]をうまくつかうと回避できる場合もあるかもしれないけれど、よく分かりません。
[2.0系]
モジュールロードの順番を間違えると、 proxyが先に実行され、rewriteは行われません。
svr2に対して、/foo/ へのリクエストが発行され、その結果、404 Not Found となります
[2.2系]
RewriteRuleの第二引数にhttp://svr1/を付け忘れると、 403 Forbidden となります。
rewrite log に、prefixing with document_root of /bar/ FAILED が出力されます。
[2.0系、2.2系]
RewriteRuleの第二引数にhttp://svr1/が無く、かつ、第三引数に[P]があると、
svr2に対して、/bar/ へのリクエストが発行され、その結果、404 Not Found となります。

また、debian-sargeのapache2パッケージ(apache-2.0.54)では、ソースからコンパイルした2.0.54ではOKだった組み合わせでもNGになりました。設定ファイル上ではrewrite, proxyの順でモジュールをロードしてもrewriteが実行されていないように見えます。
また、proxyを使わずにrewriteのみを使ったときも、rewrite logのファイルは生成されるけど中身は空のままです。
詳しい理由は、まだよく分かりませんが、もしなにか分かったらまた書こうと思います。