Apacheリバースプロキシ入門: ProxyMatchとRewriteRuleの使い分け
mod_proxyとは?
mod_proxy
は、Apache HTTP Serverをプロキシサーバーとして機能させるための主要なモジュールです。フォワードプロキシ(クライアントから見て外部のサーバーにアクセスする際に間に立つ)とリバースプロキシ(クライアントから見て、内部のサーバーへのアクセスをApacheが仲介する)の両方をサポートします。通常、Webアプリケーションサーバー(例:Tomcat, Node.jsアプリなど)の前にApacheを置いて、静的コンテンツの配信や負荷分散、SSL終端などを担当させるリバースプロキシとして利用されることが多いです。
<ProxyMatch>
の役割
通常、ProxyPass
ディレクティブを使って、特定のパス(例:/app/
)へのリクエストをバックエンドサーバーに転送するように設定します。
ProxyPass /app/ http://backend.example.com:8080/app/
ProxyPassReverse /app/ http://backend.example.com:8080/app/
しかし、より複雑なURLパターンに対してプロキシを行いたい場合、単一のパスだけでは対応しきれないことがあります。このような場合にProxyMatch
ディレクティブが役立ちます。
ProxyMatch
は、ProxyPass
の正規表現版と考えることができます。リクエストURLが指定した正規表現にマッチした場合に、そのリクエストをバックエンドサーバーに転送します。
構文例
<ProxyMatch "^/users/([0-9]+)/profile$">
ProxyPassMatch "^/users/([0-9]+)/profile$" "http://backend.example.com/api/users/$1/details"
ProxyPassReverse "^/users/([0-9]+)/profile$" "http://backend.example.com/api/users/$1/details"
</ProxyMatch>
上記の例では、以下のことを行っています。
-
ProxyPassReverse "^/users/([0-9]+)/profile$" "http://backend.example.com/api/users/$1/details"
:- これはリバースプロキシにおいて非常に重要なディレクティブです。バックエンドサーバーからのHTTPレスポンスに含まれる
Location
、Content-Location
、URI
などのヘッダーを書き換えます。 - もしバックエンドがリダイレクトなどで内部のURL(例:
http://backend.example.com/api/users/123/details
)を返した場合、クライアントがそのURLに直接アクセスしようとするのを防ぎ、Apacheプロキシ経由でアクセスするようにURLを書き換えます。これにより、クライアントは常にApacheプロキシを通してサービスにアクセスしているかのように見えます。
- これはリバースプロキシにおいて非常に重要なディレクティブです。バックエンドサーバーからのHTTPレスポンスに含まれる
-
ProxyPassMatch "^/users/([0-9]+)/profile$" "http://backend.example.com/api/users/$1/details"
:- このディレクティブは、マッチしたリクエストを
http://backend.example.com/api/users/$1/details
に転送します。 $1
は、正規表現の1番目のキャプチャグループ(ここではユーザーIDに当たる数字)を参照します。これにより、/users/123/profile
のようなリクエストが/api/users/123/details
としてバックエンドに転送されます。
- このディレクティブは、マッチしたリクエストを
-
<ProxyMatch "^/users/([0-9]+)/profile$">
:- このブロック内の設定が、リクエストURLが正規表現
^/users/([0-9]+)/profile$
にマッチした場合に適用されることを示します。 ^
: 文字列の先頭/users/
: "/users/"というリテラル文字列([0-9]+)
: 1つ以上の数字の並び(この部分がキャプチャグループとなり、$1で参照可能になります)/profile$
: "/profile"というリテラル文字列と文字列の末尾
- このブロック内の設定が、リクエストURLが正規表現
なぜ<ProxyMatch>
を使うのか?
ProxyPass
だけでは対応できない、以下のような複雑なルーティング要件がある場合にProxyMatch
は非常に有効です。
- より柔軟なURLマッピングを行いたい
- 複数の異なるバックエンドサービスに、特定のパターンに基づいてルーティングしたい
- URLの一部が動的に変化する(例: ユーザーID、商品IDなど)
注意点
ProxyPassMatch
とProxyPassReverse
はセットで利用することが推奨されます。- 正規表現は強力ですが、複雑にしすぎるとパフォーマンスに影響を与える可能性があります。また、予期しないマッチを防ぐため、正確に記述することが重要です。
ProxyPassMatch
を使用する場合、通常のProxyPass
と同様に、mod_proxy
およびプロキシ対象のプロトコルに対応するモジュール(例:mod_proxy_http
など)が有効になっている必要があります。
モジュールのロード忘れ (503 Service Unavailable, No protocol handler was valid for the URL)
最も基本的なエラーの一つで、必要なmod_proxy
および関連モジュールがロードされていない場合に発生します。
エラーメッセージ例
- クライアントには
503 Service Unavailable
またはBad Gateway
が表示される。 AH01144: No protocol handler was valid for the URL /.
(Apacheのerror_logに表示されることが多い)
原因
- プロキシするプロトコルに対応するサブモジュール(例: HTTPの場合は
mod_proxy_http.so
、AJPの場合はmod_proxy_ajp.so
、WebSocketの場合はmod_proxy_wstunnel.so
)がロードされていない。 mod_proxy
自体(mod_proxy.so
)がロードされていない。
トラブルシューティング
-
httpd.confまたは適切な設定ファイルを確認
以下のLoadModule
ディレクティブが有効になっていることを確認します。コメントアウトされている場合は解除します。LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so # 必要に応じて他のサブモジュールもロード # LoadModule proxy_ajp_module modules/mod_proxy_ajp.so # LoadModule proxy_balancer_module modules/mod_proxy_balancer.so # LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
-
モジュールを有効化するコマンドの実行
多くのLinuxディストリビューションでは、a2enmod
コマンドでモジュールを有効化できます。sudo a2enmod proxy sudo a2enmod proxy_http sudo systemctl restart apache2 # または httpd
-
Apacheを再起動
設定変更を反映させるために、必ずApacheを再起動してください。
正規表現のマッチングミス (404 Not Found, 403 Forbidden, 不適切なルーティング)
<ProxyMatch>
の最も多い問題は、指定した正規表現が意図したURLにマッチしない、または意図しないURLにマッチしてしまうことです。
エラーメッセージ例
- バックエンドにリクエストが届かない、または間違ったバックエンドに転送される。
- クライアントには
403 Forbidden
(権限の問題、後述) - クライアントには
404 Not Found
(Apacheがリクエストを処理できない場合)
原因
ProxyPassReverse
が正しく設定されていないため、バックエンドからのリダイレクトがうまく処理されない。- 複数の
<ProxyMatch>
ルールが重複・競合している。 - キャプチャグループ(
()
)と参照($1
など)の不整合。 - 正規表現の記述ミス(例:
^
や$
のつけ忘れ、エスケープ漏れ)。
トラブルシューティング
-
正規表現のテスト
オンラインの正規表現テスター(例: regex101.com)などで、実際にプロキシしたいURLが正しくマッチするか、キャプチャグループが意図通りに機能するかを確認します。# 例: /users/123/profile を /api/users/123/details に変換したい場合 <ProxyMatch "^/users/([0-9]+)/profile$"> ProxyPassMatch "^/users/([0-9]+)/profile$" "http://backend.example.com/api/users/$1/details" ProxyPassReverse "^/users/([0-9]+)/profile$" "http://backend.example.com/api/users/$1/details" </ProxyMatch>
^
と$
で文字列全体にマッチさせるのが基本です。/
はそのままリテラルとして扱われます。.
は任意の一文字にマッチするので、.
自体にマッチさせたい場合は\.
とエスケープします。
-
Apacheのログレベルを上げる
httpd.conf
でLogLevel debug
を設定すると、Apacheのerror_logに詳細なプロキシの処理状況が出力されます。これでどのルールにマッチしたか、どこに転送しようとしたかなどが確認できます。LogLevel debug
注意
debug
レベルは非常に大量のログが出力されるため、問題解決後には元のログレベルに戻すことを忘れないでください。 -
ProxyPassReverseの確認
バックエンドがリダイレクトを返す場合(例: ログイン後のリダイレクト、末尾のスラッシュ追加など)、ProxyPassReverse
が正しく設定されていないと、クライアントにバックエンドの内部URLが公開されたり、リダイレクトが正しく機能しない場合があります。ProxyPassMatch
を使用している場合は、ProxyPassReverse
も同じ正規表現と置換文字列で設定する必要があります。# ProxyPassMatch と対になるように設定 ProxyPassReverse "^/users/([0-9]+)/profile$" "http://backend.example.com/api/users/$1/details"
-
ProxyPassとProxyPassMatchの競合
同じパスに対してProxyPass
とProxyPassMatch
の両方が設定されていると、予期せぬ動作を引き起こす可能性があります。より具体性の高いルールが優先されますが、混乱を避けるためにも重複は避けるべきです。
バックエンドサーバーとの接続問題 (502 Bad Gateway, 503 Service Unavailable)
Apacheは正しくリクエストを受け取っているものの、バックエンドサーバーに接続できない、またはバックエンドからの応答がない場合に発生します。
エラーメッセージ例
- Apacheのerror_logに
error reading status line from remote server
やConnection refused
など。 - クライアントには
503 Service Unavailable
(バックエンドに接続できない、またはバックエンドが利用不可) - クライアントには
502 Bad Gateway
(バックエンドからの無効な応答、または接続確立後のエラー)
原因
- Apacheのタイムアウト設定が短すぎる。
- バックエンドサーバーが過負荷で応答しない。
- DNS解決の問題(バックエンドのホスト名が解決できない)。
- ファイアウォールがApacheサーバーとバックエンドサーバー間の通信をブロックしている。
- バックエンドサーバーが起動していない、またはリスニングポートが間違っている。
トラブルシューティング
-
バックエンドサーバーの状態確認
- バックエンドアプリケーションが実行されていることを確認します(例:
systemctl status tomcat
、pm2 status
など)。 - バックエンドアプリケーションが正しいポートでリッスンしていることを確認します(例:
netstat -tulnp | grep 8080
)。
- バックエンドアプリケーションが実行されていることを確認します(例:
-
ネットワーク接続の確認
- Apacheサーバーからバックエンドサーバーへの疎通を確認します(
ping
、telnet <backend_ip> <port>
、curl http://backend.example.com:8080/
)。 - 途中のファイアウォール(OSのfirewalld/ufw、クラウドセキュリティグループなど)がポートをブロックしていないか確認します。
- Apacheサーバーからバックエンドサーバーへの疎通を確認します(
-
DNS解決の確認
- バックエンドのホスト名を
ProxyPassMatch
で指定している場合、Apacheサーバーがそのホスト名を正しく解決できるか確認します(dig backend.example.com
)。
- バックエンドのホスト名を
-
Apacheのタイムアウト設定
ProxyTimeout
ディレクティブを設定することで、バックエンドからの応答を待つ時間を調整できます。
<!-- end list -->
ProxyTimeout 300 # 300秒 (5分) に設定 (デフォルトは60秒)
これは
VirtualHost
やProxyMatch
ブロック内、またはグローバルに設定できます。
HTTPヘッダーの問題
リバースプロキシ環境では、クライアントのIPアドレスやホスト名などの情報がバックエンドに正しく伝わらないことがあります。
エラーメッセージ例
- バックエンドアプリケーションが特定のホスト名に依存している場合に問題が発生する。
- バックエンドアプリケーションのログにクライアントのIPアドレスがApacheサーバーのIPアドレスとして記録される。
原因
RequestHeader
ディレクティブが不適切。ProxyPreserveHost On
が設定されていない。
トラブルシューティング
-
ProxyPreserveHost Onの有効化
これにより、クライアントがリクエストしたHost
ヘッダーがそのままバックエンドサーバーに転送されます。ProxyPreserveHost On
-
X-Forwarded-Forなどのヘッダーの確認
クライアントの元のIPアドレスをバックエンドに伝えるために、通常ApacheはX-Forwarded-For
ヘッダーを追加します。バックエンドアプリケーションがこれらのヘッダーを正しく読み取れるように設定されているか確認します。
コンテンツの書き換えが不完全 (画像が表示されない、CSSが適用されない)
バックエンドアプリケーションが返すHTML内のパスが、ApacheプロキシのURLと一致しない場合に発生します。ProxyPassReverse
はHTTPヘッダーを書き換えますが、HTMLコンテンツ内のURLは書き換えません。
エラーメッセージ例
- リンクをクリックすると、プロキシを経由せず直接バックエンドのURLにアクセスしようとする。
- ページは表示されるが、画像、CSS、JavaScriptなどの静的ファイルが読み込まれない(ブラウザの開発者ツールで
404 Not Found
エラーが見られる)。
原因
- 特定のフレームワークやCMSが内部パスを絶対パスで生成している。
ProxyPassReverse
はヘッダーのみを書き換えるため、HTMLボディ内のURLは書き換わらない。- バックエンドが返すHTML内の絶対パス(例:
/css/style.css
)が、プロキシのパスと合致しない。
トラブルシューティング
-
バックエンドアプリケーション側の設定変更
- 最も理想的なのは、バックエンドアプリケーションが相対パスでURLを生成するように設定するか、プロキシパスを考慮した絶対パスを生成するように設定することです。
- 例えば、プロキシが
/app/
で、バックエンドがルートパスで動作している場合、バックエンドが/css/style.css
を返すとApacheはそれをhttp://yourdomain.com/css/style.css
として処理しようとします。しかし、正しくはhttp://yourdomain.com/app/css/style.css
であるべきです。
-
mod_substituteの使用(最終手段、パフォーマンスへの影響に注意)
Apacheのmod_substitute
モジュールを使用すると、レスポンスボディの内容を正規表現で検索・置換することができます。これはHTML内のURLを書き換えるのに使えますが、パフォーマンスに影響を与える可能性があります。LoadModule substitute_module modules/mod_substitute.so <LocationMatch "^/users/([0-9]+)/profile$"> ProxyPassMatch "^/users/([0-9]+)/profile$" "http://backend.example.com/api/users/$1/details" ProxyPassReverse "^/users/([0-9]+)/profile$" "http://backend.example.com/api/users/$1/details" # 例: バックエンドが返す /css/ を /app/css/ に書き換える AddOutputFilterByType SUBSTITUTE text/html Substitute "s|href=\"/css/|href=\"/app/css/|i" </LocationMatch>
注意
これはあくまで最後の手段であり、パフォーマンスのオーバーヘッドを考慮する必要があります。可能であればバックエンド側で修正するのが望ましいです。
一般的なトラブルシューティングのヒント
- ブラウザのキャッシュをクリアする
クライアント側で古いコンテンツやリダイレクトがキャッシュされている場合があります。ブラウザのキャッシュをクリアするか、シークレットモード/プライベートウィンドウで試してみてください。 - サービス再起動を忘れずに
設定ファイルを変更した後は、必ずApacheサービスを再起動してください。sudo systemctl restart apache2
またはsudo systemctl restart httpd
- 構文チェック
設定ファイルを変更した後、Apacheを再起動する前に構文チェックを行います。sudo apachectl configtest
またはsudo httpd -t
- アクセスログを確認する
CustomLog
に設定されているログファイルは、どのリクエストがApacheに到達し、どのように処理されたか(HTTPステータスコードなど)を確認するのに役立ちます。 - Apacheのエラーログを常に確認する
ErrorLog
に設定されているログファイル(通常は/var/log/httpd/error_log
や/var/log/apache2/error.log
など)は、トラブルシューティングの最初の情報源です。LogLevel debug
に設定して詳細な情報を得ることを強くお勧めします。
前提
以下の例では、必要なmod_proxy
関連モジュールが有効になっていることを前提としています。
# 少なくともこれらのモジュールが有効になっていることを確認してください
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
# 必要に応じて、AJPの場合はmod_proxy_ajp.so、WebSocketの場合はmod_proxy_wstunnel.so なども有効化します。
例1: 特定のパスに続く数値IDをバックエンドに転送する
ユーザープロファイルのAPIなどで、URLのパスにユーザーIDのような動的な数値が含まれる場合によく使われます。
目的
http://yourdomain.com/users/123/profile
のようなリクエストを、
バックエンドサーバーの http://backend.example.com:8080/api/v1/users/123/details
にプロキシします。
設定
<VirtualHost *:80>
ServerName yourdomain.com
ProxyPreserveHost On # オリジナルのHostヘッダをバックエンドに転送
<ProxyMatch "^/users/([0-9]+)/profile$">
# 正規表現の最初の括弧 () でキャプチャされた値は、$1 で参照できます
ProxyPassMatch "^/users/([0-9]+)/profile$" "http://backend.example.com:8080/api/v1/users/$1/details"
ProxyPassReverse "^/users/([0-9]+)/profile$" "http://backend.example.com:8080/api/v1/users/$1/details"
</ProxyMatch>
# その他の静的ファイルや、他のアプリケーションへのルーティングなど
DocumentRoot "/var/www/html"
<Directory "/var/www/html">
Require all granted
</Directory>
</VirtualHost>
解説
ProxyPassReverse
も同じ正規表現と置換文字列を使うことで、バックエンドからのリダイレクトヘッダーなどが正しく書き換えられ、クライアントが直接バックエンドのURLにアクセスするのを防ぎます。ProxyPassMatch
の第2引数で、キャプチャした$1
をバックエンドのURLに組み込むことで、動的なルーティングを実現しています。^/users/([0-9]+)/profile$
は、リクエストURLの先頭が/users/
で始まり、その後に1つ以上の数字が続き(これが$1
としてキャプチャされる)、最後に/profile
で終わるURLにマッチします。
例2: APIバージョン付きのURLをプロキシする
RESTful APIでバージョン管理をURLパスで行っている場合などに便利です。
目的
http://yourdomain.com/api/v1/data
を http://backend-v1.example.com:8081/data
に、
http://yourdomain.com/api/v2/data
を http://backend-v2.example.com:8082/data
にプロキシします。
設定
<VirtualHost *:80>
ServerName yourdomain.com
ProxyPreserveHost On
# API v1 のルーティング
<ProxyMatch "^/api/v1/(.*)$">
# $1 は /api/v1/ に続く全てのパス部分をキャプチャ
ProxyPassMatch "^/api/v1/(.*)$" "http://backend-v1.example.com:8081/$1"
ProxyPassReverse "^/api/v1/(.*)$" "http://backend-v1.example.com:8081/$1"
</ProxyMatch>
# API v2 のルーティング
<ProxyMatch "^/api/v2/(.*)$">
ProxyPassMatch "^/api/v2/(.*)$" "http://backend-v2.example.com:8082/$1"
ProxyPassReverse "^/api/v2/(.*)$" "http://backend-v2.example.com:8082/$1"
</ProxyMatch>
DocumentRoot "/var/www/html"
<Directory "/var/www/html">
Require all granted
</Directory>
</VirtualHost>
解説
- これにより、異なるバージョンのAPIを異なるバックエンドサーバーに簡単にルーティングできます。
^(.*)$
は、スラッシュ以降の全てのパス部分をキャプチャし、$1
としてバックエンドのURLに渡します。
例3: WebSocketプロキシ(正規表現使用)
リアルタイム通信で使われるWebSocket接続をプロキシする場合の例です。
目的
ws://yourdomain.com/chat/room123
のようなWebSocketリクエストを、
バックエンドのWebSocketサーバー ws://backend.example.com:8080/ws/room123
にプロキシします。
設定
# mod_proxy_wstunnel モジュールをロードしていることを確認
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
<VirtualHost *:80>
ServerName yourdomain.com
ProxyPreserveHost On
<ProxyMatch "^/chat/(.*)$">
# WebSocket接続をバックエンドに転送
# upgrade=websocket は、HTTP/1.1のUpgradeヘッダーを処理し、WebSocket接続を確立するために必要
ProxyPassMatch "^/chat/(.*)$" "ws://backend.example.com:8080/ws/$1" upgrade=websocket
ProxyPassReverse "^/chat/(.*)$" "ws://backend.example.com:8080/ws/$1"
</ProxyMatch>
DocumentRoot "/var/www/html"
<Directory "/var/www/html">
Require all granted
</Directory>
</VirtualHost>
解説
ProxyMatch
の正規表現で、/chat/
以下のパスをキャプチャし、バックエンドの/ws/
以下のパスに転送しています。ws://
スキームを使用し、upgrade=websocket
パラメータを追加することで、WebSocketプロキシを有効にします。
例4: 特定の拡張子を持つファイルを特定のサーバーにプロキシする
画像やPDFなどの特定の種類のコンテンツを別のサーバーでホスティングしている場合に役立ちます。
目的
http://yourdomain.com/assets/images/photo.jpg
や
http://yourdomain.com/documents/report.pdf
のようなリクエストを、
静的ファイルサーバー http://static.example.com:8000/content/
に転送します。
設定
<VirtualHost *:80>
ServerName yourdomain.com
ProxyPreserveHost On
# 画像ファイル (.jpg, .png, .gif) のルーティング
<ProxyMatch "\.(jpg|png|gif)$">
# リクエストURLのファイル名部分をそのままバックエンドに転送
# ここでは正規表現全体がマッチ対象なので、$0 を使うことも可能
# しかし、より安全にファイル名だけを渡すために (.*) でキャプチャし $1 を使う
ProxyPassMatch "(.*)\.(jpg|png|gif)$" "http://static.example.com:8000/content/$1.$2"
ProxyPassReverse "(.*)\.(jpg|png|gif)$" "http://static.example.com:8000/content/$1.$2"
</ProxyMatch>
# PDFファイル (.pdf) のルーティング
<ProxyMatch "\.pdf$">
ProxyPassMatch "(.*)\.pdf$" "http://static.example.com:8000/documents/$1.pdf"
ProxyPassReverse "(.*)\.pdf$" "http://static.example.com:8000/documents/$1.pdf"
</ProxyMatch>
DocumentRoot "/var/www/html"
<Directory "/var/www/html">
Require all granted
</Directory>
</VirtualHost>
ProxyPassMatch
の第二引数は、プロキシ先のURLと、正規表現でキャプチャした内容を$n
で参照して動的に生成します。ProxyPassMatch
の第一引数は、リクエストURL全体にマッチする正規表現を指定します。.
は正規表現で特殊な意味を持つため、\.
とエスケープする必要があります。
- ProxyTimeout
バックエンドサーバーからの応答が遅い場合に、タイムアウト時間を調整できます。 - ProxyPreserveHost On
これは非常に重要です。クライアントから送られてきたHost
ヘッダーをそのままバックエンドに転送することで、バックエンドサーバーが正しいホスト名で応答できるようになります。 - ProxyPassとの組み合わせ
ProxyPass
とProxyPassMatch
は異なる動作をします。ProxyPass /some/path http://backend/some/path
は、/some/path
に続くパスをそのままバックエンドに転送します。ProxyPassMatch "^/some/(.*)$" "http://backend/other/$1"
は、正規表現による柔軟なパス変換を行います。- これらを併用する場合、競合に注意し、意図しないルーティングが発生しないよう慎重に設定する必要があります。
- マッチング順序
複数のProxyMatch
ディレクティブがある場合、Apacheは設定ファイル内で先に記述されたものから順にマッチングを試みます。より具体的なルールを先に記述することが重要です。 - 正規表現の複雑さ
正規表現は強力ですが、複雑にしすぎると可読性が低下し、デバッグが難しくなります。シンプルに保つことを心がけましょう。
mod_proxy: ProxyPass ディレクティブ
最も一般的でシンプルなプロキシ設定方法です。正規表現ではなく、固定のパスプレフィックスに基づいてプロキシを行います。
特徴
- パフォーマンス
正規表現のマッチングがない分、わずかに高速である可能性があります。 - 固定パス
特定のパスに始まるリクエストをすべてプロキシするのに適しています。 - シンプルさ
正規表現の知識が不要で、直感的に設定できます。
ユースケース
- 静的ファイル(例:
/static
)を別のサーバーから提供する場合。 - 特定のアプリケーション(例:
/app
)へのすべてのリクエストをバックエンドサーバーに転送する場合。
例
# mod_proxy と mod_proxy_http が有効になっていることを確認
<VirtualHost *:80>
ServerName yourdomain.com
ProxyPreserveHost On
# /app/ に始まる全てのリクエストを http://backend.example.com:8080/app/ にプロキシ
ProxyPass /app/ http://backend.example.com:8080/app/
ProxyPassReverse /app/ http://backend.example.com:8080/app/
# /api/ に始まる全てのリクエストを http://another-backend.example.com:9000/api/ にプロキシ
ProxyPass /api/ http://another-backend.example.com:9000/api/
ProxyPassReverse /api/ http://another-backend.example.com:9000/api/
DocumentRoot "/var/www/html"
<Directory "/var/www/html">
Require all granted
</Directory>
</VirtualHost>
ProxyMatch との比較
- 上記の例で、
ProxyPass
は/app/foo/bar
をhttp://backend.example.com:8080/app/foo/bar
に転送しますが、/app
の後のパスを変換することはできません。 ProxyMatch
はURLの途中の動的な部分をキャプチャしてバックエンドのURLに組み込むことができますが、ProxyPass
は固定のパス置換のみを行います。
mod_rewrite モジュール (RewriteRule と [P] フラグ)
mod_rewrite
はApacheで最も強力で柔軟なURL書き換えモジュールです。そのRewriteRule
ディレクティブの[P]
(Proxy)フラグを使用することで、正規表現に基づいてリクエストをプロキシできます。これはProxyPassMatch
と非常に似た機能を提供します。
特徴
- オーバーヘッド
非常に強力な反面、設定が複雑になりやすく、処理オーバーヘッドが大きくなる可能性があります。 - 複雑なルーティング
RewriteCond
と組み合わせることで、非常に複雑なルーティングロジックを実装できます。 - 非常に柔軟な正規表現
mod_proxy
の正規表現よりも詳細な条件(リクエストヘッダー、環境変数など)に基づいて書き換え/プロキシが可能です。
ユースケース
- まずローカルのファイルシステムをチェックし、存在しない場合にのみプロキシしたい場合(コンテンツ移行時など)。
- 特定の条件(例: クライアントのIPアドレス、ユーザーエージェントなど)に基づいてプロキシ先を変更したい場合。
- URLの途中の要素を大きく変換してプロキシする場合(例:
mod_proxy: <ProxyMatch>
と同様のシナリオ)。
例
# mod_rewrite と mod_proxy、mod_proxy_http が有効になっていることを確認
LoadModule rewrite_module modules/mod_rewrite.so
<VirtualHost *:80>
ServerName yourdomain.com
ProxyPreserveHost On
RewriteEngine On # mod_rewrite を有効にする
# mod_proxy: <ProxyMatch> の例1 と同じ機能
# /users/123/profile を http://backend.example.com:8080/api/v1/users/123/details にプロキシ
RewriteRule "^/users/([0-9]+)/profile$" "http://backend.example.com:8080/api/v1/users/$1/details" [P,L]
# ProxyPassReverse は mod_proxy の機能なので、ProxyPassMatch/RewriteRule [P] とは別に必要
ProxyPassReverse /users/ http://backend.example.com:8080/api/v1/users/ # この行は正確なパスではなく、親パスでカバーする必要がある
# 例: リクエストされたファイルが存在しない場合にのみ、古いサーバーにプロキシ
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule "^/(.*)" "http://old-site.example.com/$1" [P,L]
ProxyPassReverse / http://old-site.example.com/
DocumentRoot "/var/www/html"
<Directory "/var/www/html">
Require all granted
</Directory>
</VirtualHost>
解説
- 重要な注意点
mod_rewrite
でプロキシを行う場合でも、バックエンドからのリダイレクトヘッダーを正しく書き換えるために、対応するProxyPassReverse
ディレクティブが必要です。ProxyPassReverse
はProxyPass
やProxyMatch
のように正規表現を受け取るわけではないため、リライト先のURLの"root"となるパスをカバーする形で設定する必要があります。これはProxyPassMatch
を使用する場合よりも少し複雑になる場合があります。 [L]
フラグは、このルールが適用された場合、それ以降のRewriteRule
の処理を停止するように指示します("Last"の意)。[P]
フラグは、マッチしたリクエストをmod_proxy
を使ってプロキシするように指示します。RewriteRule
の第2引数は置換後のURLです。ここでも$1
などのキャプチャグループ参照が使えます。RewriteRule
の第1引数は正規表現で、マッチしたURLに対して操作を行います。RewriteEngine On
でmod_rewrite
を有効にします。
mod_alias: AliasMatch ディレクティブ (ローカルファイルパスへのマッピング)
これは厳密にはプロキシの代替ではありませんが、URLの正規表現マッチングという点で似た機能を提供し、ローカルファイルシステムへのマッピングに使用されます。外部サーバーへのプロキシとは異なる用途です。
特徴
- 外部リソースではない
mod_proxy
のように別のHTTPサーバーにリクエストを転送するわけではありません。 - ローカルパスへのマッピング
ウェブサーバーが提供するローカルディスク上のファイルやディレクトリを、URLの正規表現に基づいて動的に指定したい場合に利用します。
ユースケース
- 特定のファイルタイプ(例:
.css
や.js
)を、特定のディレクトリ構造に正規表現でマッピングして提供する場合。 - 複数のバージョン管理された静的アセットを、URLから動的にパスを決定して提供する場合。
例
# mod_alias が有効になっていることを確認
<VirtualHost *:80>
ServerName yourdomain.com
# /assets/v1/images/foo.jpg を /var/www/static/v1/images/foo.jpg にマッピング
# /assets/v2/css/style.css を /var/www/static/v2/css/style.css にマッピング
AliasMatch "^/assets/(v[0-9]+)/(.*)$" "/var/www/static/$1/$2"
<Directory "/var/www/static">
Require all granted
</Directory>
DocumentRoot "/var/www/html" # デフォルトのDocumentRoot
<Directory "/var/www/html">
Require all granted
</Directory>
</VirtualHost>
解説
$1
,$2
などは、正規表現のキャプチャグループを参照します。AliasMatch
は、正規表現にマッチしたURLをローカルファイルシステムのパスに変換します。
ロードバランシングとクラスタリングのための mod_proxy_balancer
これはmod_proxy: <ProxyMatch>
の直接の代替というよりも、mod_proxy
ファミリーの拡張であり、より高度なプロキシユースケースをカバーします。複数のバックエンドサーバー間でリクエストを分散させたり、フェイルオーバーを設定したりする場合に利用します。
特徴
- 動的な管理
balancer-manager
インターフェースを通じて、リアルタイムでバランサーメンバーの状態を管理できます。 - ヘルスチェック
バックエンドサーバーの健全性を監視し、ダウンしたサーバーへの転送を停止できます。 - 負荷分散
ラウンドロビン、重み付け、セッションスティッキーなど、様々な負荷分散アルゴリズムをサポートします。
ユースケース
- 複数のアプリケーションサーバーインスタンスを持つ環境。
- 高可用性またはスケーラビリティが要求されるWebアプリケーション。
例
# mod_proxy, mod_proxy_http, mod_proxy_balancer, mod_lbmethod_byrequests などが有効になっていることを確認
<VirtualHost *:80>
ServerName yourdomain.com
ProxyPreserveHost On
# バランサーの定義
<Proxy "balancer://mycluster">
BalancerMember http://backend1.example.com:8080 route=app1
BalancerMember http://backend2.example.com:8080 route=app2 loadfactor=2
# セッションスティッキー (JSESSIONID クッキーを使用)
ProxySet stickysession=JSESSIONID
</Proxy>
# 特定のパスをバランサーにプロキシ
# この例では、正規表現ではなく固定パスなので ProxyPass を使用
ProxyPass /app/ balancer://mycluster/app/
ProxyPassReverse /app/ balancer://mycluster/app/
# もし /api/v1/users/ 以下の動的なパスをバランサーに転送したい場合は ProxyPassMatch を併用
# <ProxyMatch "^/api/v1/users/(.*)$">
# ProxyPassMatch "^/api/v1/users/(.*)$" "balancer://mycluster/api/v1/users/$1"
# ProxyPassReverse "^/api/v1/users/(.*)$" "balancer://mycluster/api/v1/users/$1"
# </ProxyMatch>
DocumentRoot "/var/www/html"
<Directory "/var/www/html">
Require all granted
</Directory>
</VirtualHost>
解説
ProxyPass
またはProxyPassMatch
ディレクティブで、定義したバランサー名 (balancer://mycluster
) をプロキシ先として指定します。<Proxy "balancer://mycluster">
ブロックで、mycluster
という名前のロードバランサーを定義し、そのメンバー(バックエンドサーバー)を指定します。
mod_proxy: <ProxyMatch>
は正規表現を用いた柔軟なURLマッピングに特化していますが、Apacheには様々なプロキシおよびURL操作のオプションが存在します。
- 高可用性・負荷分散が必要なプロキシ
mod_proxy_balancer
とProxyPass
の組み合わせが推奨されます。 - ローカルファイルシステムへの動的なマッピング
AliasMatch
が適しています。 - 複雑なURL書き換えや条件付きプロキシ
mod_rewrite
が非常に強力です。 - シンプルな固定パスプロキシ
ProxyPass
が最適です。