Apache

[apache] ProxyPreserveHostをOnにしてハマる

More than 3 years have passed since last update.

apacheによるリバースプロキシを多段構成にしていて、バックエンドでもクライアントがリクエストしたホスト名を取りたい!なんてことがありました。

ProxyPreserveHostを設定すれば解決するじゃん!という答えに行き着いたのですが、ハマることがあったのでメモしておきます。

参考


やりたいこと

以下の構成をとったとき、バックエンドでクライアントがリクエストしたホスト名を取りたい。しかし多段プロキシの影響によりlocalhostが取れてしまう。

cliant -> proxy1(apache) -> proxy2(apache) -> backend


単純に対応すると

上記proxy1, proxy2のコンフィグに以下を追記すればclientがリクエストしたホスト名がそのまま渡ってきます。が、他の設定に影響を与えるため注意が必要です。


apache.conf

  ProxyPreserveHost On



ProxyPassReverseにハマる

リバースプロキシを使う際、リダイレクトの対応としてProxyPassReverseを設定することがよくあります。

しかし、以下のような書き方をしているとProxyPassReverseが効かなくなります。


apache.conf

## リバースプロキシの設定

ProxyPass /ja http://localhost:3080/ja
ProxyPassReverse /ja http://localhost:3080/ja

## リダイレクトでこんな設定があるとして
redirect /ja/hoge /ja


期待ではこうなるはず

$ curl -I http://myhost.com/ja/hoge

HTTP/1.0 302 Moved Temporarily
...
Location: http://myhost.com/ja

しかし実際は期待しないポート番号付で返ってくる

$ curl -I http://myhost.com/ja/hoge

HTTP/1.0 302 Moved Temporarily
...
Location: http://myhost.com:3080/ja

これはリクエストヘッダの値がProxyPreserveHostによって変わっているために発生するようです(X-Forwaeded-HostかHostを見てる?)

バックエンドがlocalhost:3080にリダイレクトさせるはずが、myhost:3080で返してくるため、ProxyPassReverseに引っかからなくなっています。

期待通りにリダイレクトのホスト名を書き換えようと思うと以下の対応が必要です


解決案1:ProxyPassReverseを書き換える

クライアントがリクエストするホスト名をベタっと書いてしまう


apache.conf

  ProxyPass /ja http://localhost:3080/ja

ProxyPassReverse /ja http://myhost.com:3080/ja


解決案2:バックエンドでServerNameを使う

バックエンド自身にlocalhost、もしくはクライアントがリクエストするホスト名を名乗らせることでも解決します。クライアントがリクエストするホスト名を名乗らせた場合、ProxyPassReverseの設定自体が不要になります


apache.conf

  ServerName myhost.com:80 または ServerName localhost:3080 とか

UseCanonicalName on #これをセットで記述しないと有効になりません


SSLProxyにハマる

SSLProxyを利用している場合、プロキシ先の証明書とリクエストのホスト名が不一致となりプロキシに失敗してしまいます。

例えばこんな設定を書いていたとすると、


apache.conf

  SSLProxyEngine on

ProxyPass /other https://myhost2.com/other

このようなエラーが出ます。

[ssl:info] [pid xxxx] [remote x.x.x.x:443] AH02005: SSL Proxy: Peer certificate CN mismatch: Certificate CN: myhost2.com Requested hostname: myhost.com


(暫定)解決案 とりあえず証明書の確認をスキップ

プロキシ先のサーバが信頼できる必要がありますが、証明書の確認をスキップすることで回避できます。そうでない場合は真面目に確認が必要ですが、今回はそこまで調べていません・・・。


apache.conf

  SSLProxyEngine on

SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
ProxyPass /other https://myhost2.com/other