Help us understand the problem. What is going on with this article?

リバースプロキシ(ProxyPass,ProxyPassReverse)の動作を調べてみました。

More than 1 year has passed since last update.

1.本記事について

本記事は、Apache(version:2.2)のProxyPass、ProxyPassReverseについてHTTPヘッダやログを見ながら実際に挙動を確認した内容になります。

ProxyPassについては、公式ドキュメント[1]のページを見れば大体内容がわかりましたが、
ProxyPassReverseについては分らなかったので、実際にサーバを動かして確認してみました。

2.試す

以下のようにVagrantとVirtualBoxで確認用のサーバを作りました。
また、確認用のリソースとして、webサーバにapp/foo.htmlとapp/redirect_to.htmlを準備しています。
構成は以下の通り。

env_image.PNG

Vagrantfile
Vagrant.configure(2) do |config|
  config.vm.define "client" do |node|
        node.vm.box = "opscode-centos-6.7"
        node.vm.hostname = "client"
        node.vm.network "private_network", ip: "192.168.100.100"
  end
  config.vm.define "proxy" do |node|
        node.vm.box = "opscode-centos-6.7"
        node.vm.hostname = "proxy"
        node.vm.network "private_network", ip: "192.168.100.200"
  end
  config.vm.define "web" do |node|
        node.vm.box = "opscode-centos-6.7"
        node.vm.hostname = "web"
        node.vm.network "private_network", ip: "192.168.110.200"
  end
end

Vagrantについては、皆さんよくご存じでVagrantfileを見れば雰囲気はわかると思いますので、詳細は割愛します。
今回作成したサーバの役割は

  • client:リクエストするclientサーバ
  • proxy :ゲートウェイであるproxyサーバ。clientからのリクエストをwebに転送する。
  • web :各種リソースが存在する内部webサーバ。clientからの直接のリクエストは受け付けない。

になります。

3.まずProxyPass

ProxyPassについては公式ドキュメントのページに以下のようにあります。

このディレクティブはリモートサーバをローカルサーバの名前空間に マップできるようにします。
ローカルサーバは通常の意味でのプロキシと しては動作せず、リモートサーバのミラーとして振る舞います。
ローカルサーバはしばしば リバースプロキシ や ゲートウェイ と呼ばれます。
path はローカルの仮想パスの名前です。
url は リモートサーバの部分 URL になり、クエリー文字列を含むことはできません。

やりたいことは、proxyサーバ経由でwebのapp/foo.htmlを取得できるようにすることです。
以下のようにproxyサーバをconfigします。

/etc/httpd/conf/httpd.conf
ProxyRequests Off

ProxyPass /app/ http://192.168.110.200/app/

configが完了したので、clientサーバからリクエストして確認します。

[vagrant@client ~]$ curl -v http://192.168.100.200/app/foo.html
* About to connect() to 192.168.100.200 port 80 (#0)
*   Trying 192.168.100.200... connected
* Connected to 192.168.100.200 (192.168.100.200) port 80 (#0)
> GET /app/foo.html HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: 192.168.100.200
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sun, 17 Dec 2017 13:41:12 GMT
< Server: Apache/2.2.15 (CentOS)
< Last-Modified: Sun, 17 Dec 2017 03:11:48 GMT
< ETag: "2003f3-8c-56080982c2783"
< Accept-Ranges: bytes
< Content-Length: 140
< Content-Type: text/html; charset=UTF-8
< Connection: close
<
<!DOCTYPE HTML>
<html lang="ja">
<head>
<title>HTMLのテストページ</title>
</head>
<body>
内部WEBサーバです!
</body>
</html>
* Closing connection #0

clientには問題なくコンテンツが返却されました。
またproxyサーバにはclientサーバ、webサーバにはproxyサーバ経由でリスエストが飛んでいることがわかります。

[root@proxy ~]# tail -f /var/log/httpd/access_log
192.168.100.100 - - [17/Dec/2017:13:48:00 +0000] "GET /app/foo.html HTTP/1.1" 200 140 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
[root@web ~]# tail -f /var/log/httpd/access_log
192.168.110.1 - - [17/Dec/2017:13:48:01 +0000] "GET /app/foo.html HTTP/1.1" 200 140 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"

4.続いてProxyPassReverse

以下は公式ドキュメントのページからの引用です。

このディレクティブは Apache に HTTP リダイレクト応答の Location, Content-Location, URI ヘッダの調整をさせます。
これは、Apache がリバースプロキシ (ゲートウェイ) として使われているときに、
リバースプロキシを通らないアクセスを防止するのに重要です。
このようなアクセスは、リバースプロキシの背後にいるバックエンドサーバへの HTTP リダイレクトが原因で起きます。

上記の特別なリダイレクト用の HTTP レスポンスヘッダのみが書き換えられます。
Apache は他のレスポンスヘッダを書き換えたり、HTML ページの中の URL 参照を 書き換えたりすることはありません。
つまり、リバースプロキシされた HTML ページ内に 絶対 URL 参照が存在すると、
プロキシを通さずにアクセスする可能性があります。

この説明だけ見てもなにやらわかりませんので、こちらも実際にconfigして挙動を確認します。
ポイントは、 webサーバ(=公式ドキュメントの説明でいうところのバックエンドサーバ)でリダイレクト が発生する場合です。
リダイレクトが発生しない場合はProxyPassの時に確認できたように、コンテンツをproxyサーバから取得できました。

webサーバのconfigは以下。app/redirect_from.htmlからapp/redirect_to.htmlにリダイレクトされるようにしました。

/etc/httpd/conf/httpd.conf
Redirect permanent /app/redirect_from.html /app/redirect_to.html

仮にproxyサーバのProxyPassReverseをconfigしない場合にどのような挙動になるのか確認します。
※なおclientサーバから直接webサーバへアクセスできないように
※clientサーバで事前に

[root@client ~]# route add 192.168.110.200 reject

※としています。

それでは、clientサーバからリクエストします。

[vagrant@client ~]$ curl -v -L http://192.168.100.200/app/redirect_from.html
* About to connect() to 192.168.100.200 port 80 (#0)
*   Trying 192.168.100.200... connected
* Connected to 192.168.100.200 (192.168.100.200) port 80 (#0)
> GET /app/redirect_from.html HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: 192.168.100.200
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Date: Sun, 17 Dec 2017 14:12:24 GMT
< Server: Apache/2.2.15 (CentOS)
< Location: http://192.168.110.200/app/redirect_to.html
< Content-Length: 332
< Content-Type: text/html; charset=iso-8859-1
< Connection: close
<
* Closing connection #0
* Issue another request to this URL: 'http://192.168.110.200/app/redirect_to.html'
* About to connect() to 192.168.110.200 port 80 (#0)
*   Trying 192.168.110.200... Failed to connect to 192.168.110.200: Network is unreachable
* Success
* couldn't connect to host
* Closing connection #0
curl: (7) Failed to connect to 192.168.110.200: Network is unreachable

proxyとwebには以下のようにログが出力されています。

[root@proxy ~]# tail -f /var/log/httpd/access_log
192.168.100.100 - - [17/Dec/2017:14:13:28 +0000] "GET /app/redirect_from.html HTTP/1.1" 301 332 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
[root@web ~]# tail -f /var/log/httpd/access_log
192.168.110.1 - - [17/Dec/2017:14:13:29 +0000] "GET /app/redirect_from.html HTTP/1.1" 301 332 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"

これは、clientサーバからリクエストし、proxyサーバ経由でwebサーバにアクセスしたときwebサーバでリダイレクトが発生しているため、
clientサーバに戻されたHTTPヘッダのLocationがリダイレクト先のwebサーバのURLになっていることが原因です。
実際にcurlの出力で Location: http://192.168.110.200/app/redirect_to.html となっています。
clientサーバ側でLocation先のURLにリダイレクトしようとするが、
clientサーバからのwebサーバへの直接のネットワーク経路が存在しないことから、clientサーバからのリクエストが届いていません。

ここでproxyサーバのconfigにProxyPassReverseを追加し、再度clientサーバから同じリクエストを飛ばします。

/etc/httpd/conf/httpd.conf
ProxyPassReverse /app/ http://192.168.110.200/app/
[vagrant@client ~]$ curl -v -L http://192.168.100.200/app/redirect_from.html
* About to connect() to 192.168.100.200 port 80 (#0)
*   Trying 192.168.100.200... connected
* Connected to 192.168.100.200 (192.168.100.200) port 80 (#0)
> GET /app/redirect_from.html HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: 192.168.100.200
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Date: Sun, 17 Dec 2017 14:27:34 GMT
< Server: Apache/2.2.15 (CentOS)
< Location: http://192.168.100.200/app/redirect_to.html
< Content-Length: 332
< Content-Type: text/html; charset=iso-8859-1
< Connection: close
<
* Closing connection #0
* Issue another request to this URL: 'http://192.168.100.200/app/redirect_to.html'
* About to connect() to 192.168.100.200 port 80 (#0)
*   Trying 192.168.100.200... connected
* Connected to 192.168.100.200 (192.168.100.200) port 80 (#0)
> GET /app/redirect_to.html HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: 192.168.100.200
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Sun, 17 Dec 2017 14:27:34 GMT
< Server: Apache/2.2.15 (CentOS)
< Last-Modified: Sun, 17 Dec 2017 11:43:55 GMT
< ETag: "200407-a7-56087bfa32aae"
< Accept-Ranges: bytes
< Content-Length: 167
< Content-Type: text/html; charset=UTF-8
< Connection: close
<
<!DOCTYPE HTML>
<html lang="ja">
<head>
<title>HTMLのテストページ</title>
</head>
<body>
内部WEBサーバでリダイレクトしました!
</body>
</html>
* Closing connection #0
192.168.100.100 - - [17/Dec/2017:14:27:34 +0000] "GET /app/redirect_from.html HTTP/1.1" 301 332 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
192.168.100.100 - - [17/Dec/2017:14:27:34 +0000] "GET /app/redirect_to.html HTTP/1.1" 200 167 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
192.168.110.1 - - [17/Dec/2017:14:27:34 +0000] "GET /app/redirect_from.html HTTP/1.1" 301 332 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"
192.168.110.1 - - [17/Dec/2017:14:27:34 +0000] "GET /app/redirect_to.html HTTP/1.1" 200 167 "-" "curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.19.1 Basic ECC zlib/1.2.3 libidn/1.18 libssh2/1.4.2"

curlから出力されたLocation句が、 Location: http://192.168.100.200/app/redirect_to.html となっており、
clientサーバで、リダイレクトされたリクエストがproxyサーバ経由で再度リクエストされています。
結果として、clientサーバにリダイレクト先のコンテンツがproxyサーバ経由で取得できました。

5.まとめ

ApacheのProxyPass、ProxyPassReverseについて、HTTPヘッダやログを中心に挙動を確認しました。
ProxyPassReverseについてはproxy先のサーバでリダイレクトが発生した時にHTTPヘッダのLocation句を調整していることが分かりました。

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした