reverse proxyをHTTP/HTTPS両対応のサイトに導入するときの落とし穴

発生にいたるメカニズム

サーバ構成
フロントのリバースクプロキシにNginx、バックエンドのアプリケーションサーバにApacheを使用していました。
(ただ、これは何のソフトウェアを使っているかとかは関係なく、どの組み合わせでも起こりうると思います。)

フロントでNginxが80番と443番の両方のポートをListenして、バックエンドでApacheが8080番をListenします。
わりとよくある構成ではないかと思います。
ブラウザ -> (http)  -> Nginx(80)  -> (http) -> Apache(8080)
ブラウザ -> (https) -> Nginx(443) -> (http) -> Apache(8080)
Nginxは、クライアントから静的コンテンツを要求された場合は即レスポンスし、動的コンテンツ(.phpなど)を要求された場合は背後のApacheにリクエストを転送します。
このとき、NginxとApache間の通信はHTTPで行われます。
ここがハマリポイントです。

何が問題なのか?

バックエンドサーバが受けるリクエストが常にHTTPであるということです。
アプリケーションの作りが悪い場合、下記のような絶対URLのscript要素を吐いてしまうことがあります。
<script src="http://..." ></script>
このような作りになっていると、ブラウザがHTTPSでアクセスしてきたときにもHTTPのURLを吐いてしまい、スクリプトが読み込まれずバグになります。

もうこの時点で「URLは相対パスで書け」とか「ネットワークパス参照(network-path reference)」使え」という声が聞こえてきそうですね。
もちろん自分でアプリを書くときはそのようにします。

いい加減、<script src="http://.. と書くのはやめましょう

問題は他人が書いたアプリを運用するときとか、CMSを使っている場合です。
CMSやCMSプラグインの中には上記のような絶対URLを吐くものが割とよくある気がします。

対処法

いろいろ選択肢はあります。
その1:相対パスで書く
これが王道でしょう。
しかしアプリがCMSの場合、例えばWordPressのソースコードを改変したりプラグインを改修したりというのはなかなか大変です。
仮に改修できたとしてもアップグレードのときにまた同じ改修をしないといけなくなります。
その2:フロンとバックの間にもHTTPSを使う
ブラウザがHTTPで来たらバックもHTTPで、ブラウザがHTTPSで来たらバックもHTTPSで通信します。
ブラウザ -> (http)  -> Nginx(80)  -> (http) -> Apache(8080)
ブラウザ -> (https) -> Nginx(443) -> (https) -> Apache(444)
これならまあ動くかもしれません。
ただこれもデメリットがあって、

  • バックエンド側にもSSL証明書を組み込まないといけない
  • Nginx <-> Aapache間のSSLのオーバーヘッドがある
  • アプリ側が443番以外のHTTPSに対応してない場合がある。
実際、WordPressが443じゃないHTTPSだと動かなくて泣きました。
その3:常にsrc="https://..." を吐く
<script src="https://..." ></script>
この方法なら割と安全です。
デメリットは、SSL証明書の期限が切れたときにHTTPページも死んでしまうことでしょうか。

WordPressをこの方法で動かす方法は次の記事で紹介します。

まとめ

以上、リバースプロキシとHTTPS(SSL)の落とし穴の紹介でした。

私はまだリバースプロキシの経験が少ないので、何か間違っているとかもっとよい方法があるとかあれば、Twitterで教えていただけると幸いです。
@DQNEO
カテゴリ: