ApacheやNginxでHTTPS通信をリバースプロキシする

HTTPS通信のリバースプロキシ

フロントのApacheやNginxでHTTPS通信を受けてSSL終端し、バックエンドのアプリケーションへプロキシするとする。アプリケーションでリダイレクトをしようとすると、HTTPS通信をしてほしいのにLocationヘッダにHTTP通信が指定されてしまうことがある。
Jenkinsをバックエンドのアプリケーションにおいた場合を想定し、ApacheとNginxでの解決方法をそれぞれ記載する。

Apache

HTTPS通信を正しくリバースプロキシするための方法は三つある。

リバースプロキシの仕方

三つの方法について詳細を書く前に、Apacheでのリバースプロキシの仕方について簡単に書く。

リバースプロキシする場合に一般的な記述は次の二行だ。

ProxyPassディレクティブでリバースプロキシを行う。バックエンドアプリケーションを同じサーバにおいているためlocalhost:8080としているが、別のリモートサーバでも大丈夫だ。
ProxyPassReverseディレクティブでバックエンドからのリダイレクトがあった場合を対処する。このディレクティブはApacheにHTTPリダイレクト応答の Location, Content-Location, URI ヘッダの調整をさせる。これがないとアプリケーションがリダイレクトするときにバックエンドのURI(この場合はhttp://localhost:8080/jenkins)がLocationヘッダに設定されてしまう。

方法1: ProxyPassReverseを使う

それではHTTPS通信を扱う場合の方法について見ていく。
ひとつ目の方法はProxyPassReverseを注意深く使う。

バックエンドアプリケーションからhttp://ci.example.com/jenkinsにリダイレクトするように指定された場合に元々のHTTPSプロトコルとFQDNのhttps://ci.example.com/jenkinsが設定されるようなProxyPassReverseを設定している。

Jenkinsはリダイレクト時のLocationでアクセス時のFQDNで返してくるようだった。
試しにProxyPassだけ記載してcurlでアクセスすると以下のLocationが返ってきた。

このLocationにマッチするようにProxyPassReverseを書くために、ProxyPassReverse http://localhost:8080/jenkins以外にProxyPassReverse http://ci.example.com/jenkinsを書いている。

Jenkinsがlocalhost:8080でLocationヘッダを書き戻さないのは、Apacheがリバースプロキシする際に以下のヘッダを追加するが、この中のX-Forwarded-Hostをうまく扱ってくれるからか?

  • X-Forwarded-For : クライアントの IP アドレス。
  • X-Forwarded-Host : オリジナルのホスト名。クライアントが Host リクエストヘッダで渡す。
  • X-Forwarded-Server : プロキシサーバのホスト名。

アプリケーションのリダイレクト時のLocationヘッダが何になるかはアプリケーション依存のところが強く、実際の挙動を確かめて注意深く設定することになる。一般的にはProxyPassと同じURIを持つProxyPassReverseをいつでも書いておくのがいいと思う。

ちなみにpythonで簡易HTTPサーバを立ち上げてApacheのProxyPassディレクティブでプロキシしてみると、FQDNもつけずに相対パスで返ってきた。

HTTP/HTTPSのプロトコルがついていない//FQDN/PATHのようなスキーマレスURLや相対パスの場合はProxyPassReverseをつけなくても上手くHTTPSでアクセスして入ればHTTPSにリダイレクトしてくれるだろう。

方法2: Header editを使う

荒技だが方法1よりも簡単に使える。Header editでHTTPヘッダをhttpからhttpsに書き換えてしまえばいい。

Locationのhttp://https://に置換されているのがわかる。

方法3: X-Forwarded-Protoを使う

バックエンドのアプリケーションの実装次第となるが、X-Forwarded-Protoヘッダを加えると、そこで指定されたプロトコルでアクセスされていると判断してリダイレクトのLocation等を生成してくれる。
Ruby on RailsやJenkinsは対応しているが、対応していないアプリケーションも多い。

X-Forwarded-Proto以外にX-Forwarded-Portもあり、なくても動くが念のためHTTPSのポート443を指定しておくのがいい。

Apacheにおけるベストな方法

個人的に思うApacheにおけるベストは方法2だ。

方法1は素直にApacheのリバースプロキシ関連のディレクティブを使っているが具体的にバックエンドのアプリケーションがどのようなURLをLocationにセットするのか確認する必要がある。方法3はデファクトスタンダードといってもいいX-Forwarded系のヘッダを使っているものの、RFC標準ではないため全てのアプリケーション/フレームワークが対応しているとも限らない。

方法2のLocationヘッダを書き換えてしまうという方法が強引に見えても一番確実かつ汎用的に思う。

Nginx

NginxはApacheより簡単だ。方法は二つある。

方法1: proxy_redirectを使う

proxy_redirectでhttp://https://に変えることができる。ApacheのHeader edit Locationと内容は同じだが、標準でNginxが用意しているものなのでApacheの時のような強引さがない。

これがNginxにおいて個人的にベストな方法であるのはApacheからの流れで言うまでもない。

方法2: X-Forwarded-Protoを使う

Apacheの方法3と同様のことがNginxでもできる。

-Linux
-, ,