Apacheを使ってReverseProxyしていて、フロント(クライアント)からのアクセスをhttpsで、バックエンドをhttpでやっているようなケースでの問題。mod_proxyのドキュメント通りに、おおむね以下のような設定を書いているハズ。
<VirtualHost *:443> (...snip...) ProxyPass /bugzilla http://192.168.1.3/bugzilla ProxyPassReverse /bugzilla http://192.168.1.3/bugzilla (...snip...) </VirtualHost>
ProxyPassReverseは、バックエンドがLocationヘッダでリダイレクトしようとするような場合に、pathをReverseProxyっぽく(クライアントの都合のいいように)書き換えるオプション。このとき、Locationが相対パスなら特に問題ないものの、絶対パス(特にhttp://がある)場合に問題になる。つまり、ReverseProxyはpathは書き換えるもののプロトコルはhttpのままとなり、クライアントはhttpsからhttpにリダイレクトされてしまう。たぶんhttp/httpsの関係が逆でも同じことが起こるハズ。
これを解決する方法はググるとおおむね2つ見つかる。1つめは「もう一度リダイレクトする」ことで、つまり、クライアントがhttpでアクセスしたらhttpsにアクセスするようにしておく。何度もリダイレクトするのがムダなものの、負荷さえ気にしなければそれほど悪くもない。ただ、デフォルトポート(httpを80, httpsを433)にやっているならこういう対処も可能だけど、ポート番号を変更している場合、ポート番号はそのままにプロトコルだけ切り替わってしまい、httpsで待ち受けるポートにhttpでアクセスされることになってしまうので、この方法は使えない。
もう1つが「X-FORWARDED-PROTO」を使う方法で、ReverseProxyがバックエンドにリクエストするとき「X_FORWARDED_PROTO: 'https'」を付加することでバックエンドのアプリに指示を出してバックエンドのアプリに適切なLocation: を吐かせるようにする。
<VirtualHost *:443> (...snip...) RequestHeader set X_FORWARDED_PROTO 'https' (...snip...) </VirtualHost>
ただ、「X-FORWARDED-PROTO」はあくまでアプリに指示をするだけのもので、Ruby on Rails だとこれを解釈してくれるものの、bugzillaなどそうでないアプリの方が圧倒的に多い気がする。ReverseProxyがnginxの場合だと「proxy_redirect http:// https://;」なんて方法があるようだけど、Apacheだとあまり適切な設定項目がない。いろいろ悩んだ結果、力業であることを承知で下記のように対処。
<VirtualHost *:443> (...snip...) Header edit Location ^http https ProxyPass /bugzilla http://192.168.1.3/bugzilla ProxyPassReverse /bugzilla http://192.168.1.3/bugzilla (...snip...) </VirtualHost>
HeaderディレクティブについてはApacheのmod_headersの英語ドキュメントを参照。なぜか日本語版には Header editが書いてない。日本語のドキュメントだけ見てたときは「Apache 2.4からか?」と勘違いしてしまった。