15
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

OSSTechAdvent Calendar 2018

Day 4

mod_proxy_htmlを使ってみた

Last updated at Posted at 2018-12-04

前回はmod_proxy_htmlについて説明しました。今回はmod_proxy_htmlの具体的な設定について載せていきます。
mod_prxoy_htmlはリバースプロキシサーバーに組み込むモジュールのため、前回の説明に使った構成図の環境を作成する流れで説明します。

システム構成

システム構成はmod_proxy_htmlがインストールされたリバースプロキシ、アプリケーション1、アプリケーション2の3つの環境です。
リバースプロキシの環境は CentOS7 を使用します。
アプリケーション1、アプリケーション2は何でも良いのでWebサーバーを動かしておきます。(本記事では割愛します。)

qiita-mod_proxy_html1.png

mod_proxy_htmlのインストール

mod_proxy_html を実際に試してみましょう。mod_proxy_html は CentOS7 の標準パッケージに含まれているのでyumコマンドでインストールを行うことができます。

  • mod_proxy_htmlインストール
# yum install mod_proxy_html

yum でインストールすると/etc/httpd/conf.modules.d/00-proxyhtml.confが配置されますので、Apacheの再起動によってモジュールがロードされる構成となります。

それではhttpd.conf に mod_proxy_html の設定を行います。

httpd.confの設定

リバースプロキシサーバーのhttpd.confに設定を記載します。(本記事ではリバースプロキシに関連した設定のみ扱います。)

URL変換対象のHTMLのタグ

URLの変換を行うHTMLタグの設定です。

ProxyHTMLLinks  a               href
ProxyHTMLLinks  area            href
ProxyHTMLLinks  link            href
ProxyHTMLLinks  img             src longdesc usemap
ProxyHTMLLinks  object          classid codebase data usemap
ProxyHTMLLinks  q               cite
ProxyHTMLLinks  blockquote      cite
ProxyHTMLLinks  ins             cite
ProxyHTMLLinks  del             cite
ProxyHTMLLinks  form            action
ProxyHTMLLinks  input           src usemap
ProxyHTMLLinks  head            profile
ProxyHTMLLinks  base            href
ProxyHTMLLinks  script          src for

ProxyHTMLLinksがURL変換を行うHTMLタグの指定を行うディレクティブです。
第一引数には要素名、第二引数以降には要素に対して適用する属性名を定義します。属性名は複数設定可能です。
上記はサンプルとして設定例を載せていますが、実際の環境ではアプリケーションのURLのリンクとして使用しているHTMLタグに漏れがないか確認して、必要があれば追加します。

共通設定

mod_proxy_htmlを使用するにあたっての共通の設定です。

<Location />
  RequestHeader unset Accept-Encoding
  <IfModule mod_filter.c>
    FilterProvider iconv    xml2enc "%{CONTENT_TYPE} =~ m|^text/html|"
    FilterProvider proxyhtml   proxy-html "%{CONTENT_TYPE} =~ m|^text/html|"
    FilterProvider SUBSTITUTE SUBSTITUTE "%{CONTENT_TYPE} =~ m|^text/html|"
    FilterProvider SUBSTITUTE SUBSTITUTE "%{CONTENT_TYPE} =~ m|^text/css|"
    FilterProvider SUBSTITUTE SUBSTITUTE "%{CONTENT_TYPE} =~ m|^text/javascript|"
    FilterProvider SUBSTITUTE SUBSTITUTE "%{CONTENT_TYPE} =~ m|^application/javascript|"
    FilterChain SUBSTITUTE iconv proxyhtml
  </IfModule>
</Location>

<Location />で囲んだ中に共通設定を行っています。
RequestHeader unset Accept-Encoding 1はアプリケーションの応答コンテンツで「コンテンツ圧縮」が行われないようにするためにリクエストHTTPヘッダーから削除しています。アプリケーションからの応答コンテンツがコンテンツ圧縮されてしまうとバイナリデータであるためにmod_proxy_htmlやmod_substituteによるURLの変換が行えないためです。

FilterProviderFilterChainで、Apacheのinputフィルタに関する設定を行っています。対象のフィルターはmod_proxy_html mod_substitute mod_xml2encです。

mod_proxy_html mod_xml2encは、HTMLコンテンツのみに対してURLの変換を行うため、Content-Type が text/html のみ動作する設定を行います。
mod_substitute はmod_proxy_htmlでURLの変換が行えない場合に補完する役割で使用し、JavaScript や CSS 等のコンテンツに対しても URL の変換を行います。そのため、これらのContent-Type を設定しています。環境によっては他にもxmlファイル等必要な Content-Typeを設定する必要があります。

FilterChain では、前回の記事で触れたように mod_substitute mod_xml2enc mod_proxy_html の順番でフィルタが動作するようにしています。

Proxy先アプリケーション単位の設定

最後にアプリケーションサーバー単位の設定です。

ProxyPass /ap1/ https://ap1.example.co.jp/
<Location /ap1/>
  ProxyPassReverse https://ap1.example.co.jp/
  ProxyPassReverseCookiePath / /ap1/
  ProxyHTMLCharsetOut *
  ProxyHTMLURLMap ^/([^/])|^/$ /ap1/$1 R
  Substitute 's|//ap1.example.co.jp/|//rp.example.co.jp/ap1/|n'
</Location>

ProxyPass /ap2/ http://ap2.example.co.jp/
<Location /ap2/>
  ProxyPassReverse https://ap2.example.co.jp/
  ProxyPassReverseCookiePath / /ap2/
  ProxyHTMLCharsetOut *
  ProxyHTMLURLMap ^/([^/])|^/$ /ap2/$1 R
  Substitute 's|//ap2.example.co.jp/|//rp.example.co.jp/ap2/|n'
</Location>

ProxyPass系 の設定はmod_proxy_htmlの設定ではなく、アプリケーションサーバーへproxyするリバースプロキシとしての設定です。下記3つの設定を行っています。

  • ProxyPass で接続先のサーバーの設定
  • ProxyPassReverse では、アプリケーションが応答するLocationヘッダーの書き換え設定
  • ProxyPassReverseCookiePath では、アプリケーションが発行するSet-CookieヘッダーのPath属性の書き換え設定

ProxyHTMLCharsetOut は、URLの変換後の文字エンコーディングの指定の設定です。*と設定することで、mod_proxy_htmlで URL の変換を行った後で元のコンテンツと同じ文字エンコーディングに戻ります。この設定がなければユーザーに返るコンテンツの文字エンコーディングはUTF-8となります。

ProxyHTMLURLMap で、URLの変換ルールを設定をしています。
//【FQDN】/ のようにスキームが省略されたリンクに対してURLの変換が行われないように正規表現の変換ルールを書いています。
/ /ap1/ と書く記載例が見受けられますが、この設定方法だと //【FQDN】/ のリンクに対しても URL 変換されてしまいます。ただし、正規表現の記述は一見するとわかりづらい設定であり、コンテンツ内に//【FQDN】/と書くリンクがなければ / /ap1/ と設定しても良いと思います。

Substitute は、mod_proxy_htmlではなくmod_substituteの設定です。mod_proxy_htmlで変換できない箇所のURLの変換を行うために設定します。

以上で設定が完了しました。Apacheを再起動して設定値を反映させます。

動作確認

アプリケーション1に link.html を配置して、https://rp.example.co.jp/ap1/link.htmlへアクセスしてリンクが正しく辿れるか確認してみましょう。 link.html の文字エンコーディングは UTF-8 で作成してください。

link.html
<html><head>
<title>AP1リンク</title>
</head><body>
<a href="/top.html">トップへ(/から始まる絶対パス)</a><br>
<a href="./top.html">トップへ(相対パス)</a><br>
<a href="//ap1.example.co.jp/top.html">トップへ(//から始まる絶対パス)</a><br>
<a href="/">/だけのリンク</a><br>
<a href="https://ap1.example.co.jp/top.html">httpから始まる絶対パスのリンク</a>
</body></html>

表示されたlink.htmlのリンクをクリックするとhttps://rp.example.co.jp/ap1/〜となればmod_proxy_htmlの設定がうまく出来ています。

文字化け?

https://rp.example.co.jp/ap1/link.htmlへのアクセスで文字化けした方はいらっしゃいませんか?
もしアプリケーション1のWebサーバー等で Content-Type に 文字エンコーディングがセットされていなければ2文字化けが発生します。

qiita_mod_proxy_html_1204_1.png

これはmod_xml2encの処理でコンテンツの文字エンコーディングが何か判別できなかったため、デフォルトのISO-8859-1で文字エンコーディング変換が行われたためです。

mod_xml2enc.c
    if (!HAVE_ENCODING(ctx->xml2enc)) {
        cfg = ap_get_module_config(r->per_dir_config, &xml2enc_module);
        if (!ctx->encoding) {
            ctx->encoding = cfg->default_charset?cfg->default_charset:"ISO-8859-1";
        }

文字エンコーディングが何か判別できなった場合のデフォルト値はディレクティブが用意されているため、設定により ISO-8859-1 から変更することが可能です。
文字化けが発生した環境でhttpd.confに以下の設定を追加すると文字化けしなくなります。

xml2EncDefault UTF-8

一般的なWebアプリケーションであれば「文字エンコーディングの指定が無い」なんて事はないと思いますが、このように設定で対処することができます。
もちろん設定を行わずとも、WebサーバーでレスポンスHTTPヘッダーに文字エンコーディングをセットする3、HTMLのMETAタグで文字エンコーディングをセットする4でも文字化けを回避することができます。

ProxyHTMLEnable は使わない

本記事の設定では mod_proxy_htmlを有効にするためのProxyHTMLEnable onの設定を行っていません。mod_filterによってmod_proxy_htmlを動作させています。これは意図したもので理由は2つあります。

  1. inputフィルターで動作するフィルタの順番をコントロールしたい
  2. 不要な Content-Type で mod_xml2enc が動作するのを防ぎたい

「1」についてはすでに説明済みですので「2」の理由を説明します。
ProxyHTMLEnable onとすると自動的に mod_xml2enc も動作するよう Apache の inputフィルタに登録されます。そしてmod_xml2encのソースコードを見ると、mod_xml2encは Content-Type が text/ で始まるか、xmlという文字列が含まれると動作します。

mod_xml2enc.c
    /* only act if starts-with "text/" or contains "xml" */
    if (strncmp(ctype, "text/", 5) && !strstr(ctype, "xml"))  {
        ap_remove_output_filter(f);
        return ap_pass_brigade(f->next, bb) ;
    }

URLの変換はHTMLのコンテンツに対してのみ行うため、mod_xml2encが動作するのはtext/htmlだけで問題ありません。しかし、ProxyHTMLEnable onとすると本来必要の無いコンテンツに対しても文字エンコーディングの変換処理が実行されてしまうためこれを防ぐためにProxyHTMLEnable onの設定を行わないようにしています。

終わりに

リバースプロキシの環境でmod_proxy_htmlを使用する設定例を説明しました。
もし実際の環境でmod_proxy_htmlを使う場合は、必要なHTMLタグの設定mod_substituteの動作対象Content-Type置換文字列の設定など調整が必要です。リバースプロキシとアプリケーションを構築したあとで接続試験を行って URL の変換に漏れがないかの確認を行ってください。構築後に調整が必要となるのでテストの期間を十分に確保するようスケジュールを立てることが望ましいです。

最後にmod_proxy_htmlはどうしても使わなければならない場合のみ使いましょう。この記事を読んでいただければわかりますが、mod_proxy_htmlを使いこなすのは大変です。
もしこれからシステムを設計する状態であるのならば、mod_proxy_htmlを使わない選択肢を取れないか考慮することをオススメします。

  1. 2021/4/20訂正。Accept-Languageと記載していましたが正しくはAccept−Encodingです。

  2. WebサーバーがApacheであればAddDefaultCharsetoffと設定

  3. WebサーバーがApacheであればAddDefaultCharsetUTF-8と設定

  4. link.htmlに<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">の記述を追加

15
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?