Apache ProxyPassReverseでリダイレクト問題を解決!設定とトラブルシューティング
Apache HTTP Serverのmod_proxy
モジュールは、リバースプロキシを構築する際に非常に重要な機能を提供します。その中でもProxyPassReverse
ディレクティブは、特にバックエンドサーバーからの応答を適切に処理するために不可欠です。
リバースプロキシとは?
まず、ProxyPassReverse
が使われる「リバースプロキシ」について簡単に説明します。
リバースプロキシとは、クライアントからのリクエストを代わりに受け取り、それを内部の別のサーバー(バックエンドサーバー、またはオリジンサーバーと呼びます)に転送し、バックエンドサーバーからの応答をクライアントに返すサーバーのことです。
クライアントはリバースプロキシに対してリクエストを送信するため、バックエンドサーバーの存在を直接知る必要はありません。これにより、セキュリティの向上、負荷分散、SSL終端、キャッシュなどの機能を提供できます。
例
- Apacheがその応答をクライアントに返します。
- アプリケーションサーバーが応答を生成し、Apacheに返します。
- Apacheはリクエストを内部のアプリケーションサーバー(例:
http://localhost:8080/myapp/
)に転送します。 - Apacheリバースプロキシがこれを受け取ります。
- クライアントが
http://www.example.com/app/
にアクセスします。
ProxyPass
ディレクティブ
ProxyPassReverse
を理解する前に、ProxyPass
ディレクティブについて触れておきましょう。
ProxyPass
は、特定のリクエストパスをバックエンドサーバーのURLにマッピングするために使用されます。
構文
ProxyPass [リクエストパス] [バックエンドURL]
例
ProxyPass /app/ http://localhost:8080/myapp/
この設定は、「http://www.example.com/app/
へのリクエストは、http://localhost:8080/myapp/
に転送する」という意味になります。
ProxyPassReverse
ディレクティブの役割
ProxyPassReverse
は、ProxyPass
と組み合わせて使用されます。その主な役割は、バックエンドサーバーからのHTTP応答ヘッダー(特にLocation:
ヘッダー)を書き換えることです。
バックエンドサーバーがリダイレクト(例: 302 Found
)を返す場合、そのLocation:
ヘッダーにはバックエンドサーバー自身のURL(例: http://localhost:8080/myapp/login
)が含まれていることがあります。しかし、クライアントはバックエンドサーバーの存在を知らないため、このURLに直接アクセスすることはできません。
ProxyPassReverse
は、このような場合にLocation:
ヘッダー内のURLを、クライアントがアクセスしているリバースプロキシの公開URL(例: http://www.example.com/app/login
)に自動的に書き換えます。これにより、クライアントは適切にリダイレクトされ、バックエンドサーバーの内部アドレスを知ることなく、サービスを継続して利用できます。
構文
ProxyPassReverse [リクエストパス] [バックエンドURL]
例
ProxyPass
と合わせて通常このように設定されます。
ProxyPass /app/ http://localhost:8080/myapp/
ProxyPassReverse /app/ http://localhost:8080/myapp/
この設定により、例えばバックエンドサーバーが Location: http://localhost:8080/myapp/success
というヘッダーを返した場合、Apacheはそれを Location: http://www.example.com/app/success
に書き換えてクライアントに送信します。
ProxyPassReverse
が特に役立つケース
- セッション管理
セッション管理において、バックエンドサーバーが生成するセッションIDや関連するURLが正しくクライアントに伝わるようにするために重要です。 - クッキーのドメイン書き換え
一部のアプリケーションでは、クッキーのドメインがバックエンドサーバーの内部ドメインに設定されていることがあります。ProxyPassReverse
は、そのようなクッキーもリバースプロキシのドメインに書き換えることで、クライアントがクッキーを正しく利用できるようにします(ただし、これはProxyPassReverseCookieDomain
やProxyPassReverseCookiePath
といった別のディレクティブで詳細な制御が必要です)。 - リダイレクトの処理
バックエンドサーバーが認証後に特定のページにリダイレクトする場合など、Location:
ヘッダーを書き換えることで、クライアントが正しくアクセスできるようにします。
ProxyPassReverse
はリバースプロキシの重要なディレクティブですが、設定ミスやバックエンドサーバーの挙動によって問題が発生することがよくあります。ここでは、一般的なエラーとそのトラブルシューティング方法を説明します。
リダイレクトが正しく行われない (クライアントがバックエンドの内部URLにリダイレクトされる)
症状
ブラウザでリバースプロキシのURLにアクセスすると、バックエンドサーバーからのリダイレクトが発生した際に、ブラウザのアドレスバーにリバースプロキシのURLではなく、バックエンドサーバーの内部URL(例: http://localhost:8080/myapp/login
)が表示されてしまう。これにより、クライアントはバックエンドに直接アクセスしようとしてエラーになったり、ページが見つからなかったりします。
原因
ProxyPassReverse
ディレクティブが正しく設定されていないか、完全に欠落している可能性があります。ProxyPassReverse
は、バックエンドサーバーが返すLocation:
ヘッダーを書き換える役割を担っています。
トラブルシューティング
-
Apacheのリロード/再起動
設定ファイルを変更した後は、Apacheをリロードまたは再起動して変更を適用しているか確認してください。 -
HTTPヘッダーの確認
開発者ツール(ChromeのDevToolsなど)でネットワークタブを確認し、リダイレクトが発生した際のHTTP応答ヘッダーのLocation:
を確認します。もし内部URLがそのままになっている場合は、ProxyPassReverse
が機能していません。 -
ProxyPassReverseディレクティブの確認
ProxyPass
ディレクティブに対応するProxyPassReverse
ディレクティブが設定されているか確認してください。両者のパスは一致している必要があります。誤った例
ProxyPass /app/ http://localhost:8080/myapp/ # ProxyPassReverseが設定されていない
ProxyPass /app/ http://localhost:8080/myapp/ ProxyPassReverse /app/ http://localhost:8080/myapp/
HTMLコンテンツ内のリンクが正しく書き換わらない
症状
ページは表示されるが、そのページ内の画像、CSS、JavaScriptなどのリンク(<a>
, <img>
, <link>
, <script>
タグなど)がバックエンドサーバーの内部URLを参照しており、正しく表示されない、または機能しない。
原因
ProxyPassReverse
はHTTPヘッダー(主にLocation:
ヘッダー)を書き換える機能しか持っていません。HTMLコンテンツのボディ部分に埋め込まれたURLは書き換えません。バックエンドアプリケーションが絶対パス(例: /css/style.css
)ではなく、内部の完全なURL(例: http://backend-server:8080/css/style.css
)でリソースを生成している場合にこの問題が発生します。
トラブルシューティング
-
mod_rewriteの利用 (一部のケース)
複雑なケースでは、mod_rewrite
を使用して、プロキシが返すコンテンツの一部を書き換えることも考えられますが、これはHTMLのパースを伴わないため、確実性に欠け、パフォーマンスに影響を与える可能性があります。 -
mod_proxy_htmlの利用
Apacheのmod_proxy_html
モジュールを使用すると、HTMLコンテンツ内のURLを動的に書き換えることができます。これは強力ですが、設定が複雑になる場合があります。設定例
# mod_proxy_html を有効にする LoadModule proxy_html_module modules/mod_proxy_html.so LoadModule xml2enc_module modules/mod_xml2enc.so # 必要に応じて <Location /app/> ProxyPass http://localhost:8080/myapp/ ProxyPassReverse http://localhost:8080/myapp/ ProxyHTMLURLMap http://localhost:8080/myapp/ /app/ ProxyHTMLURLMap /myapp/ /app/ SetOutputFilter INFLATE;DEFLATE;SUBSTITUTE;proxy-html # AddOutputFilterByType text/html proxy-html # 古いApacheの場合 RequestHeader unset Accept-Encoding # 圧縮されていると書き換えが難しい場合がある </Location>
ProxyHTMLURLMap [元のURL] [新しいURL]
:HTML内のURLを書き換えるルールを定義します。SetOutputFilter proxy-html
:proxy-html
フィルタを適用してHTMLを書き換えるようにします。RequestHeader unset Accept-Encoding
:バックエンドサーバーが圧縮されたコンテンツを返す場合、mod_proxy_html
が処理できないことがあります。このヘッダーを削除することで、非圧縮のコンテンツを要求し、書き換えを可能にします。
-
バックエンドアプリケーションの設定変更
最も良い解決策は、バックエンドアプリケーションが生成するURLを相対パスにするか、またはリバースプロキシの公開URLを考慮した絶対パス(例:/app/css/style.css
)で生成するように変更することです。
SSL/TLS関連の問題 (HTTPSリバースプロキシ)
症状
HTTPSでリバースプロキシを設定しているが、バックエンドサーバーへの接続でエラーが発生したり、クライアントに証明書のエラーが表示されたりする。
原因
- SNI (Server Name Indication) の問題。
- バックエンドサーバーのSSL証明書が、Apacheの信頼ストアにないか、証明書検証に失敗している。
- Apacheがバックエンドサーバーへの接続にHTTPSを使用するように設定されていない。
トラブルシューティング
-
ProxyPreserveHost On
バックエンドサーバーがHost
ヘッダーに依存して動作する場合(例: 仮想ホストの設定)、ProxyPreserveHost On
を設定することで、クライアントから受け取ったHost
ヘッダーをそのままバックエンドに転送できます。ProxyPreserveHost On
-
バックエンド証明書の検証
本番環境では、SSLProxyVerify require
とSSLProxyCACertificateFile
を使用して、バックエンドサーバーの証明書を検証することを強く推奨します。これにより、中間者攻撃などを防ぎます。 -
SSLProxyEngine Onの設定
ApacheがバックエンドサーバーへのSSL接続を確立できるようにするために必要です。<VirtualHost *:443> ServerName www.example.com SSLEngine On SSLCertificateFile /path/to/your_certificate.crt SSLCertificateKeyFile /path/to/your_private.key ProxyPass /app/ https://localhost:8443/myapp/ ProxyPassReverse /app/ https://localhost:8443/myapp/ SSLProxyEngine On # これが重要 # 必要に応じてバックエンド証明書の検証を設定 # SSLProxyVerify require # SSLProxyCACertificateFile /path/to/backend_ca_certificate.crt </VirtualHost>
-
mod_sslとmod_proxy_httpの有効化
HTTPSリバースプロキシにはこれらのモジュールが必須です。LoadModule ssl_module modules/mod_ssl.so LoadModule proxy_http_module modules/mod_proxy_http.so
-
ProxyPassとProxyPassReverseでhttps://を指定
バックエンドがHTTPSである場合、ProxyPass
とProxyPassReverse
のバックエンドURLもhttps://
にする必要があります。ProxyPass /app/ https://localhost:8443/myapp/ ProxyPassReverse /app/ https://localhost:8443/myapp/
ProxyPassとProxyPassReverseの順序、またはLocationブロックとの関係
症状
設定が正しいはずなのにリバースプロキシが動作しない、または意図しないパスにリクエストが転送される。
原因
Apacheの設定ファイル(httpd.conf
や.conf
ファイル)におけるディレクティブの順序、特にProxyPass
やProxyPassReverse
の順序が影響する場合があります。Apacheは最も具体的なパスに一致するルールを優先します。
トラブルシューティング
-
<Location>または<VirtualHost>ブロック内での設定
ProxyPass
やProxyPassReverse
は、通常<VirtualHost>
または<Location>
ブロック内で使用されます。これにより、特定のドメインやパスに対してプロキシ設定を適用できます。<VirtualHost *:80> ServerName example.com ProxyRequests Off # フォワードプロキシを無効にする(リバースプロキシでは通常Off) <Proxy *> Require all granted # アクセス制御 </Proxy> <Location /app/> ProxyPass http://localhost:8080/myapp/ ProxyPassReverse http://localhost:8080/myapp/ </Location> </VirtualHost>
-
より具体的なパスを先に記述
例えば、/app/api/
と/app/
の両方をプロキシする場合、/app/api/
のようなより具体的なパスを先に記述します。ProxyPass /app/api/ http://localhost:8080/myapp/api/ ProxyPassReverse /app/api/ http://localhost:8080/myapp/api/ ProxyPass /app/ http://localhost:8080/myapp/ ProxyPassReverse /app/ http://localhost:8080/myapp/
mod_proxy関連モジュールの有効化忘れ
症状
Apacheのエラーログに「No protocol handler was valid for the URL」のようなエラーが表示される、またはプロキシが全く機能しない。
原因
mod_proxy
のサブモジュールが有効になっていない可能性があります。ProxyPass
とProxyPassReverse
を使用するには、mod_proxy
に加えて、使用するプロトコルに応じたサブモジュールが必要です。
トラブルシューティング
-
Apacheの再起動
モジュールを有効にした後は、必ずApacheを再起動してください。 -
必要なモジュールの有効化
- HTTPプロキシの場合:
mod_proxy.so
とmod_proxy_http.so
- AJPプロキシの場合:
mod_proxy.so
とmod_proxy_ajp.so
- WebSocketsプロキシの場合:
mod_proxy.so
とmod_proxy_wstunnel.so
- ロードバランシングの場合:
mod_proxy_balancer.so
とmod_lbmethod_byrequests.so
など
httpd.conf
または対応する設定ファイルで、以下の行のコメントアウトを解除して有効にします。LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so
(Linuxディストリビューションによっては
a2enmod
コマンドで有効化します。例:sudo a2enmod proxy proxy_http
) - HTTPプロキシの場合:
タイムアウト関連のエラー (502 Bad Gatewayなど)
症状
クライアントに502 Bad Gateway
や504 Gateway Timeout
エラーが表示される。
原因
バックエンドサーバーからの応答が遅い、またはバックエンドサーバーがダウンしている可能性があります。Apacheのプロキシタイムアウト設定が短すぎる場合もこのエラーが発生します。
トラブルシューティング
-
ProxyPassのtimeoutパラメータ
ProxyPass
ディレクティブに直接timeout
パラメータを追加することもできます。ProxyPass /app/ http://localhost:8080/myapp/ timeout=300
-
Apacheのタイムアウト設定の調整
ProxyTimeout
ディレクティブを使用して、プロキシがバックエンドサーバーからの応答を待つ時間を長くすることができます。ProxyTimeout 300 # 300秒(5分)に設定
これは
VirtualHost
、Directory
、Location
、またはサーバー全体に適用できます。 -
バックエンドサーバーの稼働状況確認
バックエンドサーバーが正常に動作しているか確認してください。直接アクセスして動作するか、ログにエラーがないかを確認します。
クッキー関連の問題
症状
リバースプロキシ経由でアクセスすると、セッションが維持されない、またはログインがうまくいかない。
原因
バックエンドサーバーが発行するクッキーのドメインやパスが、クライアントがアクセスしているリバースプロキシのドメインやパスと一致しないため、クッキーが正しく保存・送信されないことがあります。
トラブルシューティング
-
ProxyPassReverseCookiePath
バックエンドが設定するクッキーのパスを、リバースプロキシの公開パスに書き換えます。ProxyPassReverseCookiePath /backend/ /public/
これらのディレクティブは、特に複雑なアプリケーションでセッション管理がクッキーに依存している場合に重要です。
-
ProxyPassReverseCookieDomain
バックエンドが設定するクッキーのドメインを、リバースプロキシの公開ドメインに書き換えます。ProxyPassReverseCookieDomain backend.example.com public.example.com
一般的なトラブルシューティングのヒント
-
バックエンドサーバーへの直接アクセス
問題がApacheの設定にあるのか、バックエンドサーバーにあるのかを切り分けるために、可能であればバックエンドサーバーに直接アクセスして、正常に動作するかどうかを確認します。 -
設定ファイルの構文チェック
設定ファイルを変更した後、Apacheを再起動する前に構文エラーがないかチェックします。apachectl configtest # または httpd -t
-
LogLevel debug
一時的にApacheのLogLevel
をdebug
に設定すると、より詳細なデバッグ情報をログに出力させることができます。ただし、本番環境ではディスクスペースを大量に消費するため、問題解決後に元に戻すことを忘れないでください。LogLevel debug
-
Apacheのエラーログとアクセスログの確認
最も重要なステップです。エラーログ(通常/var/log/apache2/error.log
や/var/log/httpd/error_log
)には、問題の根本原因に関する情報が詳細に記録されています。アクセスログもリクエストがどのように処理されているかを確認するのに役立ちます。tail -f /var/log/apache2/error.log
mod_proxy
を使ってリバースプロキシを構築する際、ProxyPassReverse
はバックエンドサーバーからのリダイレクトを適切に処理するために非常に重要なディレクティブです。ここでは、いくつかの一般的なシナリオにおける設定例を提示します。
前提条件
これらの設定例を使用する前に、以下の Apache モジュールが有効になっていることを確認してください。通常、これらは /etc/httpd/conf/httpd.conf
や /etc/apache2/mods-available/
ディレクトリ内で設定されます。
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
# 必要に応じて、他のプロキシ関連モジュールも有効にする
# LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
# LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
# LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
# LoadModule proxy_html_module modules/mod_proxy_html.so
# LoadModule xml2enc_module modules/mod_xml2enc.so
モジュールを有効にした後、Apache を再起動またはリロードすることを忘れないでください。
例: sudo systemctl restart apache2
または sudo apachectl restart
基本的なリバースプロキシ設定 (HTTP)
最も一般的な設定です。クライアントからの /app/
へのリクエストを、バックエンドサーバーの http://localhost:8080/myapp/
に転送し、バックエンドからの応答ヘッダーを書き換えます。
# httpd.conf または VirtualHost の設定ファイルに記述
<VirtualHost *:80>
ServerName www.example.com
# プロキシとして機能させるため、フォワードプロキシの機能を無効にする(推奨)
ProxyRequests Off
# クライアントからのHostヘッダーをバックエンドにそのまま転送する
# バックエンドアプリケーションがHostヘッダーに基づいて動作する場合に重要
ProxyPreserveHost On
# /app/ へのリクエストを http://localhost:8080/myapp/ へプロキシ
ProxyPass /app/ http://localhost:8080/myapp/
# バックエンドからのLocation: ヘッダーなどを書き換える
# 例: Location: http://localhost:8080/myapp/login を Location: http://www.example.com/app/login に書き換える
ProxyPassReverse /app/ http://localhost:8080/myapp/
# プロキシされた接続に関する基本的なアクセス制御
<Proxy *>
Require all granted
</Proxy>
ErrorLog ${APACHE_LOG_DIR}/app_error.log
CustomLog ${APACHE_LOG_DIR}/app_access.log combined
</VirtualHost>
HTTPSリバースプロキシ設定 (SSL/TLS終端)
ApacheでSSL/TLSを終端し、バックエンドサーバーにはHTTPで接続するシナリオです。
# mod_ssl モジュールが有効になっていることを確認
# LoadModule ssl_module modules/mod_ssl.so
<VirtualHost *:443>
ServerName secure.example.com
ProxyRequests Off
ProxyPreserveHost On
# SSL/TLS設定
SSLEngine On
SSLCertificateFile /etc/ssl/certs/your_certificate.crt
SSLCertificateKeyFile /etc/ssl/private/your_private.key
# SSLCertificateChainFile /etc/ssl/certs/your_chain.crt # 必要に応じて
# /secureapp/ へのHTTPSリクエストを http://localhost:8080/myapp/ へプロキシ
ProxyPass /secureapp/ http://localhost:8080/myapp/
ProxyPassReverse /secureapp/ http://localhost:8080/myapp/
# クッキーのドメイン書き換えが必要な場合 (バックエンドがクッキーのドメインを設定する場合)
# 例: バックエンドが .localhost にクッキーを設定した場合、.secure.example.com に書き換える
# ProxyPassReverseCookieDomain "localhost" "secure.example.com"
# ProxyPassReverseCookiePath "/myapp/" "/secureapp/" # クッキーのパス書き換えが必要な場合
<Proxy *>
Require all granted
</Proxy>
ErrorLog ${APACHE_LOG_DIR}/secureapp_error.log
CustomLog ${APACHE_LOG_DIR}/secureapp_access.log combined
</VirtualHost>
バックエンドもHTTPSの場合
Apacheとバックエンドサーバー間の通信もHTTPSで行うシナリオです。
# mod_ssl と mod_proxy_http モジュールが有効になっていることを確認
<VirtualHost *:443>
ServerName secure.example.com
ProxyRequests Off
ProxyPreserveHost On
# SSL/TLS設定 (クライアントとの通信用)
SSLEngine On
SSLCertificateFile /etc/ssl/certs/your_certificate.crt
SSLCertificateKeyFile /etc/ssl/private/your_private.key
# ApacheがバックエンドへのSSL接続を開始できるようにする
SSLProxyEngine On
# バックエンドサーバーの証明書を検証する場合 (本番環境で推奨)
# SSLProxyVerify require
# SSLProxyCACertificateFile /etc/ssl/certs/backend_ca.crt
# /secureapp/ へのHTTPSリクエストを https://localhost:8443/myapp/ へプロキシ
ProxyPass /secureapp/ https://localhost:8443/myapp/
ProxyPassReverse /secureapp/ https://localhost:8443/myapp/
<Proxy *>
Require all granted
</Proxy>
ErrorLog ${APACHE_LOG_DIR}/secureapp_error.log
CustomLog ${APACHE_LOG_DIR}/secureapp_access.log combined
</VirtualHost>
HTMLコンテンツ内のURLを書き換える (mod_proxy_html の利用)
ProxyPassReverse
はHTTPヘッダーのみを書き換えますが、HTMLコンテンツ内のURLを書き換えたい場合はmod_proxy_html
を使用します。
# mod_proxy_html と mod_xml2enc モジュールが有効になっていることを確認
# LoadModule proxy_html_module modules/mod_proxy_html.so
# LoadModule xml2enc_module modules/mod_xml2enc.so
<VirtualHost *:80>
ServerName www.example.com
ProxyRequests Off
ProxyPreserveHost On
<Location /app/>
ProxyPass http://localhost:8080/myapp/
ProxyPassReverse http://localhost:8080/myapp/
# HTMLコンテンツ内のURLを書き換える
# 例: <a href="http://localhost:8080/myapp/page1.html"> を <a href="/app/page1.html"> に
# 例: <img src="/myapp/images/logo.png"> を <img src="/app/images/logo.png"> に
ProxyHTMLURLMap http://localhost:8080/myapp/ /app/
ProxyHTMLURLMap /myapp/ /app/
# 出力フィルタとして proxy-html を適用
# 圧縮されているコンテンツの書き換えを可能にするために Accept-Encoding を削除
SetOutputFilter INFLATE;DEFLATE;proxy-html
RequestHeader unset Accept-Encoding
Require all granted
</Location>
ErrorLog ${APACHE_LOG_DIR}/app_html_error.log
CustomLog ${APACHE_LOG_DIR}/app_html_access.log combined
</VirtualHost>
注意
mod_proxy_html
はHTMLをパースして書き換えるため、処理負荷が高くなる可能性があります。また、JavaScriptで動的に生成されるURLや、CSSファイル内のURLは書き換えられない場合があります。可能な限り、バックエンドアプリケーション側で正しい相対パスまたはプロキシ後のURLを生成するように設定するのが理想的です。
ロードバランシング設定 (複数のバックエンドサーバー)
mod_proxy_balancer
を使用して、複数のバックエンドサーバーにリクエストを分散し、各バックエンドからの応答ヘッダーも適切に処理します。
# mod_proxy_balancer, mod_lbmethod_byrequests (または他のロードバランシングメソッド) が有効になっていることを確認
<VirtualHost *:80>
ServerName lb.example.com
ProxyRequests Off
ProxyPreserveHost On
# バランサーの定義
<Proxy balancer://mycluster>
BalancerMember http://backend1.local:8080/myapp/ loadfactor=1
BalancerMember http://backend2.local:8080/myapp/ loadfactor=1
# もし特定のURLでセッション維持が必要なら
# ProxySet stickysession=JSESSIONID|jsessionid nofailover=On
ProxySet lbmethod=byrequests # リクエスト数に基づくロードバランシング
</Proxy>
# /clusterapp/ へのリクエストをバランサーに転送
ProxyPass /clusterapp/ balancer://mycluster/
# バックエンドからのLocation: ヘッダーなどを書き換える
# ここでのバックエンドURLは、BalancerMember のパスではなく、ProxyPass のターゲットとなるバランサー名
# バランサーの内部URLは ProxyPassReverse のターゲットになりません
# ProxyPassReverse は、クライアントから見えるURLと、元のバックエンドURLをペアで指定します
ProxyPassReverse /clusterapp/ http://backend1.local:8080/myapp/
ProxyPassReverse /clusterapp/ http://backend2.local:8080/myapp/
# バランサー経由の場合でも、個々のバックエンドURLに対してProxyPassReverseを設定する必要があります。
# バックエンドがリダイレクトを返した場合、Location: ヘッダーにはそのバックエンド自身のURLが含まれるためです。
<Proxy *>
Require all granted
</Proxy>
ErrorLog ${APACHE_LOG_DIR}/lb_error.log
CustomLog ${APACHE_LOG_DIR}/lb_access.log combined
</VirtualHost>
注意
ロードバランシング環境におけるProxyPassReverse
は、通常、すべてのBalancerMember
のバックエンドURLに対して個別に設定する必要があります。バックエンドがリダイレクトを返した場合、そのLocation:
ヘッダーは特定のバックエンドの内部URLを参照するためです。
ProxyPassReverse
は非常に便利ですが、以下のような状況では他の方法を検討する必要があるかもしれません。
- アプリケーション自体でのURL生成の制御
- Apache以外のリバースプロキシソリューションの利用
- より複雑なURL書き換えロジック
- HTTPヘッダー以外のコンテンツ(HTMLボディ、JavaScriptなど)内のURL書き換え
Apache mod_proxy_html を利用したコンテンツ書き換え
ProxyPassReverse
がヘッダーのみを対象とするのに対し、mod_proxy_html
はHTMLコンテンツ内のURLを書き換えることに特化したモジュールです。これは、バックエンドアプリケーションが絶対パス(例: http://internal-backend:8080/image.jpg
)でリソースのURLを生成してしまう場合に非常に有効です。
特徴
href
,src
,action
などのHTML属性だけでなく、@import
やurl()
などのCSS内のURLも対象にできる。- HTMLドキュメントをパースし、指定された正規表現に基づいてURLを書き換える。
設定例
LoadModule proxy_html_module modules/mod_proxy_html.so
LoadModule xml2enc_module modules/mod_xml2enc.so # 必要に応じて
<Location /app/>
ProxyPass http://localhost:8080/myapp/
ProxyPassReverse http://localhost:8080/myapp/ # ヘッダー書き換えは併用
# HTML内のURLを書き換えるルール
# 旧URL: http://localhost:8080/myapp/ を 新URL: /app/ に書き換え
ProxyHTMLURLMap http://localhost:8080/myapp/ /app/
# 旧URL: /myapp/ を 新URL: /app/ に書き換え (絶対パスでない場合も対応)
ProxyHTMLURLMap /myapp/ /app/
# 出力フィルタとして mod_proxy_html を適用
# バックエンドが圧縮応答を返す場合、RequestHeader unset Accept-Encoding で非圧縮を要求すると確実
SetOutputFilter INFLATE;DEFLATE;proxy-html
RequestHeader unset Accept-Encoding
</Location>
注意点
- 完全ではない: JavaScriptで動的に生成されるURLや、HTML以外のファイル(JSON APIのレスポンスなど)内のURLは書き換えられません。
- パフォーマンスへの影響: HTMLをパースして書き換えるため、処理負荷が高くなる可能性があります。
Apache mod_substitute を利用した汎用的な文字列置換
mod_substitute
は、HTTP応答のボディに対して汎用的な文字列置換(正規表現も利用可能)を行うためのモジュールです。mod_proxy_html
がHTMLの構造を理解するのに対し、mod_substitute
はバイトストリームとして単純な文字列置換を行います。
特徴
mod_proxy_html
よりも単純なため、オーバーヘッドが少ない可能性がある。- HTMLだけでなく、JSON、XML、プレーンテキストなど、様々なコンテンツに適用可能。
- コンテンツタイプに関わらず、指定されたパターンで文字列を置換できる。
設定例
LoadModule substitute_module modules/mod_substitute.so
<Location /app/>
ProxyPass http://localhost:8080/myapp/
ProxyPassReverse http://localhost:8080/myapp/ # ヘッダー書き換えは併用
# 出力フィルタとして mod_substitute を適用
AddOutputFilterByType SUBSTITUTE text/html text/javascript application/json
# HTTP応答ボディ内の文字列を置換
# 例: "http://localhost:8080/myapp/" を "/app/" に置換 (大文字小文字を区別しない)
Substitute "s|http://localhost:8080/myapp/|/app/|i"
# 例: "/myapp/" を "/app/" に置換
Substitute "s|/myapp/|/app/|i"
# 必要に応じて、コンテンツが圧縮されている場合は非圧縮を要求
RequestHeader unset Accept-Encoding
</Location>
注意点
- バイナリコンテンツには不向き: テキストベースのコンテンツに限定されます。
- 誤変換のリスク: HTMLタグの属性値だけでなく、本文中の文字列なども置換対象となるため、意図しない書き換えが発生する可能性があります。
mod_rewrite を利用した複雑なリダイレクト処理 (限定的)
mod_rewrite
は非常に強力なURL書き換えモジュールですが、主にリクエストURLの書き換えや、クライアントへのリダイレクト(3xxステータスコード)生成に使用されます。 応答ヘッダーのLocation:
を書き換える用途にはProxyPassReverse
が最適であり、mod_rewrite
で代替するのは複雑で非効率的です。
ただし、バックエンドが返すLocation
ヘッダーのURLがProxyPassReverse
のシンプルな変換で対応できない場合(例えば、バックエンドが異なるサブドメインにリダイレクトする場合など)、mod_rewrite
のHeader
ディレクティブと正規表現を組み合わせて応答ヘッダーを書き換えることが理論的には可能です。
例 (複雑なケースの概念)
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule headers_module modules/mod_headers.so
<VirtualHost *:80>
ServerName www.example.com
ProxyRequests Off
ProxyPreserveHost On
RewriteEngine On
ProxyPass /app/ http://localhost:8080/myapp/
# ProxyPassReverse はここでも使用するのが推奨される
ProxyPassReverse /app/ http://localhost:8080/myapp/
# もしバックエンドが http://backend.internal.com/login のように
# 全く異なるドメインにリダイレクトしようとする場合、
# ProxyPassReverse だけでは対応できないことがある。
# その場合、HeadersOut で Location ヘッダーを直接操作する。
# これは非常に限定的なケースであり、通常は ProxyPassReverse で十分
# 応答ヘッダーのLocationを書き換える例 (ProxyPassReverseが効かない特殊な場合)
# バックエンドが Location: http://internal.backend.com/login を返す場合、
# これを http://www.example.com/app/login に書き換える
Header edit Location "^http://internal\.backend\.com/(.*)$" "http://www.example.com/app/$1"
</VirtualHost>
注意点
- 通常のリダイレクト処理には
ProxyPassReverse
が常に優先されるべきです。 mod_rewrite
のHeader
ディレクティブは、あくまでHTTPヘッダーの文字列置換を行うものです。ProxyPassReverse
のように、プロキシパスとの関連性を自動で考慮してくれるわけではないため、より慎重な正規表現の記述が必要です。
アプリケーションレベルでのURL生成の制御
最も推奨される代替方法です。バックエンドアプリケーション自身が、自分がリバースプロキシの裏側で動作していることを認識し、すべてのURL(リダイレクト、HTMLコンテンツ内のリンク、APIレスポンス内のURLなど)を、クライアントがアクセスする公開URL(リバースプロキシのURL)に基づいて生成するように設計・設定します。
特徴
- クッキーのドメインやパスなど、
ProxyPassReverse
やmod_proxy_html
では対応しきれない複雑なURL要件にも対応しやすい。 - Apacheなどのプロキシサーバーの負荷を軽減できる。
- 最も堅牢でエラーが少ない方法。
実装方法の例
- HTTPヘッダーの利用
リバースプロキシがX-Forwarded-Host
,X-Forwarded-Proto
,X-Forwarded-For
などのヘッダーをバックエンドに転送し、バックエンドアプリケーションがこれらのヘッダーを解析して自身のURLを構築する。 - Webフレームワーク/CMSの設定
多くのモダンなWebフレームワーク(Ruby on Rails, Django, Laravel, Spring Bootなど)やCMS(WordPress, Drupalなど)には、プロキシ環境で動作するための設定オプション(例:ROOT_URL
,PUBLIC_URL
,TRUSTED_PROXIES
など)が用意されています。
例 (Apache側でX-Forwardedヘッダーを転送)
<VirtualHost *:80>
ServerName www.example.com
ProxyRequests Off
ProxyPreserveHost On
# クライアントのプロトコル、ホスト、IPアドレスをバックエンドに伝える
RequestHeader set X-Forwarded-Proto "http"
RequestHeader set X-Forwarded-Host "www.example.com"
# RequestHeader set X-Forwarded-Host $host # 動的にホスト名を渡す場合
RequestHeader set X-Forwarded-For %{REMOTE_ADDR}
ProxyPass /app/ http://localhost:8080/myapp/
ProxyPassReverse /app/ http://localhost:8080/myapp/
<Proxy *>
Require all granted
</Proxy>
</VirtualHost>
バックエンドアプリケーションは、例えば X-Forwarded-Proto
が https
で、X-Forwarded-Host
が www.example.com
であれば、それらの情報を使ってURLを生成します。
別のリバースプロキシソフトウェアの利用
Apache HTTP Server以外にも、リバースプロキシ機能に特化したソフトウェアは多数あり、それらはLocation
ヘッダーの書き換えやコンテンツ書き換えに関して異なるアプローチを提供しています。
-
HAProxy
高度なロードバランシングとプロキシ機能を提供します。HTTPヘッダーやコンテンツの書き換えを細かく制御できます。frontend http_front bind *:80 mode http default_backend webservers backend webservers mode http balance roundrobin server backend1 localhost:8080 check # Locationヘッダーを書き換える http-response replace-header Location ^http://localhost:8080/(.*) http://my.public.com/\1 # コンテンツを書き換える(HAProxyは直接的なコンテンツ書き換え機能が限定的で、通常は別途Webサーバーやフィルタが必要) # ただし、http-response set-header Content-Security-Policy などでヘッダーを追加・変更できる
-
Nginx
軽量で高性能なリバースプロキシとして広く利用されています。proxy_redirect
ディレクティブでLocation
ヘッダーを書き換え、sub_filter
ディレクティブで応答ボディ内の文字列を置換できます。location /app/ { proxy_pass http://localhost:8080/myapp/; # Locationヘッダーを書き換え proxy_redirect http://localhost:8080/myapp/ /app/; # 応答ボディを書き換え sub_filter 'http://localhost:8080/myapp/' '/app/'; sub_filter_once off; # すべての出現を置換 }
ProxyPassReverse
はHTTP応答ヘッダーのURL書き換えに特化した強力なツールですが、HTMLコンテンツ内のURL書き換えにはmod_proxy_html
やmod_substitute
、またはより根本的な解決策としてアプリケーション側でのURL生成の制御が推奨されます。