〔備忘録〕 Apache のリバースプロキシで内部 WordPress サーバを公開する

自前で用意した Web サーバで、WordPress のブログサイトを公開しようと思い、
次のような構成でサーバを準備しました。


(Internet) ------- (My Web Server @DMZ) ------ ( Wordpress Server @intra-net)
[HTTPS] [HTTP]

# 一応セキュリティのために、上記の "----" の間にはルーターが入っています。

上記の構成で、インターネットに Wordpress を公開するには、
My Web Server 上でReverse Proxyを構成すればいいと思ったのですが、
これが意外に簡単にできなかったので、備忘録としてメモします。


前提条件として、My Web Server は

WordPress は、

です。


補足)
やり方(結果)だけを見たい方は、この記事の最後の方だけを読んでください。
途中は失敗談をずらずらと書いています。 f(^^;;;



=================< 失敗談 >==================


失敗1)

単純に My Web Server で Reverse Proxy の設定をしました。



ProxyRequests Off
ProxyPass "/blog/" "http://internal-wordpress-server.invalid/"
ProxyPassReverse "/blog/" "http://internal-wordpress-server.invalid/"

としたのですが、HTML 以外のコンテンツ(画像とか)が全く表示されません。

実際に、HTML のソースコードを見ると、画像等のURLが内部の Wordpress サーバアドレスがそのまま書かれていました。 orz



失敗2)

とりあえず、 Google 先生に教えを乞いました。
その中で、

という方法が紹介されていたので、
以下のように設定を追加しました。



ProxyRequests Off
ProxyPass "/blog/" "http://internal-wordpress-server.invalid/"
ProxyPassReverse "/blog/" "http://internal-wordpress-server.invalid/"
ProxyReverseHost on
ProxyPassReverseCookieDomain internal-wordpress-server.invalid My-Web-Server.invalid
ProxyPassReverseCookiePath / /

結果は、最初と同じく、画像などが表示されません。
ただ、ヘッダーを見ると Cookie の中に書かれていた internal-wordpress-server.invalid というサーバ名が、外部公開用サーバ My-Web-Server.invalid に書き換えられていたので、この点ではちょっと進歩です。

ちなみに、ProxyReverseHostのドキュメントを見ると、
「バックエンドサーバ(この事例では Wordpress Server)が、元々の Host ヘッダを解釈する必要のあるときのような、 特別な設定が必要な場合にのみ有用です。」
と書かれていますので、
今回のケースでは必要ありませんでしたし、問題の解決にはつながりませんでした。



失敗3)

さらに、Google 先生に教えを乞うたところ、
以下のサイトを紹介していただきました。

実際に、Wordpress 内の wp-config.php に以下を追記してみました。


$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_X_FORWARDED_FOR'];

すると、動作確認で使用していた firefox 君が
「ページの自動転送設定が正しくありません
  このアドレスへのリクエストに対するサーバーの自動転送設定がループしています。
  * Cookie を無効化したり拒否していることにより、この問題が発生している可能性もあります。」
と怒ってきました。

Google先生曰く、

もあるよ。
とのことで、まずは単純に、wp-config.php


$_SERVER[‘HTTPS’]=on
を追記したところ、上記サイトの言うようにリダイレクトループは無くなりました

が、firefox 君曰く、
「internal-wordpress-server.invalid のサーバーへの接続を確立できませんでした。」
と文句を言ってきました。

当たり前ですネ。
internal-wordpress-server.invalid は HTTP 通信のみで、HTTPS は喋れません。



失敗4)

個人的に、WordpressPHP に設定以外の項目をごちゃごちゃ書くのは、
 スマートではなく好みではない。
ので、
Google 先生に再度、
 「HTTP のヘッダーだけでなく HTMLコンテンツも Reverse Proxy する方法はないの?」
と問い合わせてみました。
すると、Apache HTTPD 2.4 には mod_proxy_html があるよ

と回答いただきました。

そこで、上記失敗3)の設定は元に戻して、
Reverse Proxy 機能を提供する My Web Server で以下の設定(ProxyHTMLEnable,ProxyHTMLURLMap)を追加しました。



ProxyRequests Off
ProxyPass "/blog/" "http://internal-wordpress-server.invalid/"
ProxyPassReverse "/blog/" "http://internal-wordpress-server.invalid/"
ProxyPassReverseCookieDomain internal-wordpress-server.invalid My-Web-Server.invalid
ProxyPassReverseCookiePath / /
ProxyHTMLEnable On
ProxyHTMLURLMap http://internal-wordpress-server.invalid/ /blog/

すると、firefox 君が
「内容符号化 (Content-Encoding) に問題があります
  * 不正または不明な形式で圧縮されているため、ページを表示できません。」
と怒ってきました。

言い換えると、単純に Reverse Proxy しただけだと、HTML テキストは表示できたけど、ProxyHTMLEnable On で HTML テキストまで表示できなくなったのです。

何が違うのか…
ということで、HTML が表示できた場合と、できない場合の HTTP header を見比べてみました。
すると、

  • HTML テキストだけが表示できた場合


GET /blog/ HTTP/1.1
Host: My-Web-Server.invalid
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br

HTTP/1.1 200 OK
Server: Apache
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Type: text/html; charset=UTF-8
Content-Length: 3950

  • ProxyHTMLEnable On にして表示できなくなった場合


GET /blog/ HTTP/1.1
Host: My-Web-Server.invalid
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br

HTTP/1.1 200 OK
Date: Fri, 30 Jun 2017 02:48:38 GMT
Server: Apache
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Content-Length: 5479

と、同じ HTML テキストを gzip 圧縮しただけなはずなのに、
Content-Lnegth が違っている…

ということで、
 「WordPress サーバが Reverse Proxy に転送した HTML テキストは gzip 圧縮しており、
  更に Reverse Proxy が Client に送信したコンテンツデータは二重に gzip 圧縮している。」
という仮説が立てられます。

仮説が正しいかどうかは、
 WordPress サーバがコンテンツを圧縮しないで応答する。
ようにしてみれば、確認できるはずです。
実際にやってみたら、仮説は正解でした。



成功事例1)

WordPress サーバから送信するコンテンツを圧縮しないようにしたら、
外部 client からも閲覧できるようになりました。
その際の設定は、次のとおりです。

  • My Web Server (Reverse Proxy) の設定



ProxyRequests Off
ProxyPass "/blog/" "http://internal-wordpress-server.invalid/"
ProxyPassReverse "/blog/" "http://internal-wordpress-server.invalid/"
ProxyPassReverseCookieDomain internal-wordpress-server.invalid My-Web-Server.invalid
ProxyPassReverseCookiePath / /

ProxyHTMLEnable On
ProxyHTMLURLMap http://internal-wordpress-server.invalid/ /blog/


  • WordPress 側の apache の設定 (Ubuntu では /etc/apache2/mods-enabled/deflate.conf を下記の用に書き換える)

# /etc/apache2/mods-enabled/deflate.conf のシンボリックリンクを消すだけでもOKですね。



#
# # these are known to be safe with MSIE 6
# AddOutputFilterByType DEFLATE text/html text/plain text/xml
#
# # everything else may cause problems with MSIE 6
# AddOutputFilterByType DEFLATE text/css
# AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript
# AddOutputFilterByType DEFLATE application/rss+xml
# AddOutputFilterByType DEFLATE application/xml
#



これだと、イントラネットの Client からアクセスした場合も、
コンテンツが圧縮されなくなります。
実害はあまりないのですが、もうちょっとスマートにしたいですね。

ということで、Reverse Proxy した場合だけ圧縮しないように
する方法を調べたら、こんなヒントがありました。

方法としては、SetEnvIfNoCase ディレクティブで、
「 HTTP ヘッダ内で X-Forwarded-Host が設定されていたら、圧縮しない」
ようにしたら、うまくできました。

その設定は、以下の「最終成功時の設定」に書いておきます。



お礼>
失敗事例を全部読んでくださった皆様へ
貴重なお時間を割いていただき、ありがとうございました。




=================< 最終成功時の設定 >==================

ということで、ずらずらと途中経過を書きましたが、
結論としては 公開用Webサーバ (My Web Server) では

  • Request Header を Reverse Proxy した上で、
  • コンテンツ内も Reverse Proxy して URI を書き換え

内部の WordPress サーバでは、

  • Reverse Proxy サーバからのリクエストに対してはコンテンツを圧縮しない

ように設定すると、成功しました。

  • My Web Server (Reverse Proxy) の設定



ProxyRequests Off
ProxyPass "/blog/" "http://internal-wordpress-server.invalid/"
ProxyPassReverse "/blog/" "http://internal-wordpress-server.invalid/"
ProxyPassReverseCookieDomain internal-wordpress-server.invalid My-Web-Server.invalid
ProxyPassReverseCookiePath / /

ProxyHTMLEnable On
ProxyHTMLURLMap http://internal-wordpress-server.invalid/ /blog/


  • WordPress 側の apache の設定 (Ubuntu では /etc/apache2/mods-enabled/deflate.conf を下記の用に書き換える)




# these are known to be safe with MSIE 6
AddOutputFilterByType DEFLATE text/html text/plain text/xml

# everything else may cause problems with MSIE 6
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/xml
SetEnvIfNoCase X-Forwarded-Host .+$ no-gzip dont-vary



実例は、 https://grasso.homeip.net/blog/ にて公開しています…



※ 2017.7.8 追記

Reverse Proxy サーバ側で以下のように "SetOutputFilter INFLATE"を追記すると、
WordPress 側での "SetEnvIfNoCase" 設定は不要になります。
(但し、Reverse Proxy 側では「inflate -> deflate」処理をする可能性があり、負荷が掛ります…)



ProxyRequests Off
ProxyPass "/blog/" "http://internal-wordpress-server.invalid/"
ProxyPassReverse "/blog/" "http://internal-wordpress-server.invalid/"
ProxyPassReverseCookieDomain internal-wordpress-server.invalid My-Web-Server.invalid
ProxyPassReverseCookiePath / /

SetOutputFilter INFLATE
ProxyHTMLEnable On
ProxyHTMLURLMap http://internal-wordpress-server.invalid/ /blog/