Apacheリバースプロキシ入門: ProxyMatchとRewriteRuleの使い分け

2025-05-26

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レスポンスに含まれるLocationContent-LocationURIなどのヘッダーを書き換えます。
    • もしバックエンドがリダイレクトなどで内部のURL(例: http://backend.example.com/api/users/123/details)を返した場合、クライアントがそのURLに直接アクセスしようとするのを防ぎ、Apacheプロキシ経由でアクセスするようにURLを書き換えます。これにより、クライアントは常にApacheプロキシを通してサービスにアクセスしているかのように見えます。
  • 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"というリテラル文字列と文字列の末尾

なぜ<ProxyMatch>を使うのか?

ProxyPassだけでは対応できない、以下のような複雑なルーティング要件がある場合にProxyMatchは非常に有効です。

  • より柔軟なURLマッピングを行いたい
  • 複数の異なるバックエンドサービスに、特定のパターンに基づいてルーティングしたい
  • URLの一部が動的に変化する(例: ユーザーID、商品IDなど)

注意点

  • ProxyPassMatchProxyPassReverseはセットで利用することが推奨されます。
  • 正規表現は強力ですが、複雑にしすぎるとパフォーマンスに影響を与える可能性があります。また、予期しないマッチを防ぐため、正確に記述することが重要です。
  • 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)がロードされていない。

トラブルシューティング

  1. 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
    
  2. モジュールを有効化するコマンドの実行
    多くのLinuxディストリビューションでは、a2enmodコマンドでモジュールを有効化できます。

    sudo a2enmod proxy
    sudo a2enmod proxy_http
    sudo systemctl restart apache2 # または httpd
    
  3. Apacheを再起動
    設定変更を反映させるために、必ずApacheを再起動してください。

正規表現のマッチングミス (404 Not Found, 403 Forbidden, 不適切なルーティング)

<ProxyMatch>の最も多い問題は、指定した正規表現が意図したURLにマッチしない、または意図しないURLにマッチしてしまうことです。

エラーメッセージ例

  • バックエンドにリクエストが届かない、または間違ったバックエンドに転送される。
  • クライアントには 403 Forbidden (権限の問題、後述)
  • クライアントには 404 Not Found (Apacheがリクエストを処理できない場合)

原因

  • ProxyPassReverseが正しく設定されていないため、バックエンドからのリダイレクトがうまく処理されない。
  • 複数の<ProxyMatch>ルールが重複・競合している。
  • キャプチャグループ(())と参照($1など)の不整合。
  • 正規表現の記述ミス(例: ^$のつけ忘れ、エスケープ漏れ)。

トラブルシューティング

  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>
    
    • ^$で文字列全体にマッチさせるのが基本です。
    • /はそのままリテラルとして扱われます。
    • .は任意の一文字にマッチするので、.自体にマッチさせたい場合は\.とエスケープします。
  2. Apacheのログレベルを上げる
    httpd.confLogLevel debugを設定すると、Apacheのerror_logに詳細なプロキシの処理状況が出力されます。これでどのルールにマッチしたか、どこに転送しようとしたかなどが確認できます。

    LogLevel debug
    

    注意
    debugレベルは非常に大量のログが出力されるため、問題解決後には元のログレベルに戻すことを忘れないでください。

  3. ProxyPassReverseの確認
    バックエンドがリダイレクトを返す場合(例: ログイン後のリダイレクト、末尾のスラッシュ追加など)、ProxyPassReverseが正しく設定されていないと、クライアントにバックエンドの内部URLが公開されたり、リダイレクトが正しく機能しない場合があります。 ProxyPassMatchを使用している場合は、ProxyPassReverseも同じ正規表現と置換文字列で設定する必要があります。

    # ProxyPassMatch と対になるように設定
    ProxyPassReverse "^/users/([0-9]+)/profile$" "http://backend.example.com/api/users/$1/details"
    
  4. ProxyPassとProxyPassMatchの競合
    同じパスに対してProxyPassProxyPassMatchの両方が設定されていると、予期せぬ動作を引き起こす可能性があります。より具体性の高いルールが優先されますが、混乱を避けるためにも重複は避けるべきです。

バックエンドサーバーとの接続問題 (502 Bad Gateway, 503 Service Unavailable)

Apacheは正しくリクエストを受け取っているものの、バックエンドサーバーに接続できない、またはバックエンドからの応答がない場合に発生します。

エラーメッセージ例

  • Apacheのerror_logに error reading status line from remote serverConnection refused など。
  • クライアントには 503 Service Unavailable (バックエンドに接続できない、またはバックエンドが利用不可)
  • クライアントには 502 Bad Gateway (バックエンドからの無効な応答、または接続確立後のエラー)

原因

  • Apacheのタイムアウト設定が短すぎる。
  • バックエンドサーバーが過負荷で応答しない。
  • DNS解決の問題(バックエンドのホスト名が解決できない)。
  • ファイアウォールがApacheサーバーとバックエンドサーバー間の通信をブロックしている。
  • バックエンドサーバーが起動していない、またはリスニングポートが間違っている。

トラブルシューティング

  1. バックエンドサーバーの状態確認

    • バックエンドアプリケーションが実行されていることを確認します(例: systemctl status tomcatpm2 statusなど)。
    • バックエンドアプリケーションが正しいポートでリッスンしていることを確認します(例: netstat -tulnp | grep 8080)。
  2. ネットワーク接続の確認

    • Apacheサーバーからバックエンドサーバーへの疎通を確認します(pingtelnet <backend_ip> <port>curl http://backend.example.com:8080/)。
    • 途中のファイアウォール(OSのfirewalld/ufw、クラウドセキュリティグループなど)がポートをブロックしていないか確認します。
  3. DNS解決の確認

    • バックエンドのホスト名をProxyPassMatchで指定している場合、Apacheサーバーがそのホスト名を正しく解決できるか確認します(dig backend.example.com)。
  4. Apacheのタイムアウト設定

    • ProxyTimeoutディレクティブを設定することで、バックエンドからの応答を待つ時間を調整できます。

    <!-- end list -->

    ProxyTimeout 300 # 300秒 (5分) に設定 (デフォルトは60秒)
    

    これはVirtualHostProxyMatchブロック内、またはグローバルに設定できます。

HTTPヘッダーの問題

リバースプロキシ環境では、クライアントのIPアドレスやホスト名などの情報がバックエンドに正しく伝わらないことがあります。

エラーメッセージ例

  • バックエンドアプリケーションが特定のホスト名に依存している場合に問題が発生する。
  • バックエンドアプリケーションのログにクライアントのIPアドレスがApacheサーバーのIPアドレスとして記録される。

原因

  • RequestHeaderディレクティブが不適切。
  • ProxyPreserveHost Onが設定されていない。

トラブルシューティング

  1. ProxyPreserveHost Onの有効化
    これにより、クライアントがリクエストしたHostヘッダーがそのままバックエンドサーバーに転送されます。

    ProxyPreserveHost On
    
  2. 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)が、プロキシのパスと合致しない。

トラブルシューティング

  1. バックエンドアプリケーション側の設定変更

    • 最も理想的なのは、バックエンドアプリケーションが相対パスでURLを生成するように設定するか、プロキシパスを考慮した絶対パスを生成するように設定することです。
    • 例えば、プロキシが/app/で、バックエンドがルートパスで動作している場合、バックエンドが/css/style.cssを返すとApacheはそれをhttp://yourdomain.com/css/style.cssとして処理しようとします。しかし、正しくはhttp://yourdomain.com/app/css/style.cssであるべきです。
  2. 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/datahttp://backend-v1.example.com:8081/data に、 http://yourdomain.com/api/v2/datahttp://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.jpghttp://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との組み合わせ
    ProxyPassProxyPassMatchは異なる動作をします。
    • 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/barhttp://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ディレクティブが必要です。ProxyPassReverseProxyPassProxyMatchのように正規表現を受け取るわけではないため、リライト先のURLの"root"となるパスをカバーする形で設定する必要があります。これはProxyPassMatchを使用する場合よりも少し複雑になる場合があります。
  • [L] フラグは、このルールが適用された場合、それ以降のRewriteRuleの処理を停止するように指示します("Last"の意)。
  • [P] フラグは、マッチしたリクエストをmod_proxyを使ってプロキシするように指示します。
  • RewriteRule の第2引数は置換後のURLです。ここでも$1などのキャプチャグループ参照が使えます。
  • RewriteRule の第1引数は正規表現で、マッチしたURLに対して操作を行います。
  • RewriteEngine Onmod_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_balancerProxyPassの組み合わせが推奨されます。
  • ローカルファイルシステムへの動的なマッピング
    AliasMatchが適しています。
  • 複雑なURL書き換えや条件付きプロキシ
    mod_rewriteが非常に強力です。
  • シンプルな固定パスプロキシ
    ProxyPassが最適です。