2005-09-14 の「Content Negotiation でリクエストに応じて gzip 圧縮ファイルを返す」の続き。
cl.xml にリクエストされたときは Content Negotiation が働かず、拡張子が一切ない cl にアクセスされたときは Content Negotiation が働くという挙動だった。これでは使いにくいので、mod_rewrite を使って圧縮ファイルを返すようにする。
RewriteRule の各行の説明。
%{HTTP:Accept-Encoding} gzip は、クライアントの HTTP リクエストヘッダ中に Accept-Encoding が存在し、かつ文字列 gzip が含まれているかどうかをチェックしている。単なる正規表現によるチェックなので sonic64gzip などという文字列でもマッチしちゃうけど、まあいいでしょ。ちなみに、Accpet-Encodind: sonic4gzip で実際にリクエストして RewriteLog を見たら matched になってた。
RewriteCond %{REQUEST_FILENAME} !\.gz$ は、最初から .gz ファイルをアクセスされた場合は Rewrite しないための判定。これをやらないと、file.html.gz にアクセスがあった場合に file.html.gz.gz を探しに行ってしまう。もっとも、次の RewriteCond %{REQUEST_FILENAME}\.gz -s での gz ファイル存在チェックで弾かれるから、やらなくてもとりあえずは動く。あ、でもこれだと png や .jpg もチェック対象になるよなあ。やっぱり .html や .xml だけを対象として明示した方がいいかなあ。
RewriteCond %{REQUEST_FILENAME}\.gz -s は、.gz ファイルが存在するかどうかをチェックしている。.gz ファイルが存在しない場合は Rewrite せずにもとのリクエストのファイルを返す。
RewriteLog の出力内容。
mod_rewrite で任意の HTTP レスポンスヘッダを生成させることってできるのかな? 環境変数の値を設定することはできるけど、直接ヘッダに追加する方法は見つけられなかった。あ、HEADER ディレクティブを使えばいいのか。以下のような感じかな?
ただ、これだといつも Vary を送ってしまうので、レスポンスに Content-Encoding: gzip があるときだけ Vary を返すようにしたいけど、環境変数の値に応じて条件分岐できるのは Apache 2.0 系だけ? ドキュメントを読んでみる。
mod_headers - Apache HTTP サーバ Header ディレクティブ
http://httpd.apache.org/docs/2.1/mod/mod_headers.html#header
Apache module mod_headers
http://httpd.apache.org/docs/1.3/mod/mod_headers.html
1.3 系のドキュメントには env= という構文が使えるとは書いてないもんなあ。どちらにせよ、mod_header のモジュールがステータス: Extension じゃあ使えない可能性が高い。
そもそも Vary ヘッダの付加って 必須? 推奨? HTTP1.1 を規定している RFC 2616 ではどうなってる?
ハイパーテキスト転送プロトコル -- HTTP/1.1
http://www.studyinghttp.net/cgi-bin/rfc.cgi?2616#Sec14.44
「含まなければならない」ではなく、「含むべきである」なので必須ではないということか。じゃあ、とりあえずは Vary なしでいいか。
追記。
Internet Explorer がクライアントとき、HTTP レスポンスで Vary ヘッダを返してしまうと、IE は HTTP リクエストヘッダ If-Modified-Since を送信してこないという問題があるようだ。その結果、あらゆるレスポンスを 200 OK で返さざるを得なくなり、304 Not Modified によるレスポンスを返せなくなる。Vary を送らなければこの問題は回避できるようなので、Content-Encoding しか返さない方が総合的に見れば問題が少ないかも。
cl.xml にリクエストがあったとき、Accept-Encoding に gzip があり、かつ cl.xml.gz がサーバに存在していたら Content-Encoding: gzip で cl.xml.gz の中身を返すようにしたい
cl.xml にリクエストされたときは Content Negotiation が働かず、拡張子が一切ない cl にアクセスされたときは Content Negotiation が働くという挙動だった。これでは使いにくいので、mod_rewrite を使って圧縮ファイルを返すようにする。
- mod_rewrite を利用した Content Negotioation の代替の .htaccess サンプル
以下の RewriteRule を書いた。とりあえず希望の動作は実現できた。RewriteEngine on
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME} !\.gz$
RewriteCond %{REQUEST_FILENAME}\.gz -s
RewriteRule .+ %{REQUEST_URI}.gz
RewriteRule の各行の説明。
%{HTTP:Accept-Encoding} gzip は、クライアントの HTTP リクエストヘッダ中に Accept-Encoding が存在し、かつ文字列 gzip が含まれているかどうかをチェックしている。単なる正規表現によるチェックなので sonic64gzip などという文字列でもマッチしちゃうけど、まあいいでしょ。ちなみに、Accpet-Encodind: sonic4gzip で実際にリクエストして RewriteLog を見たら matched になってた。
(4) RewriteCond: input='sonic64gzip' pattern='gzip' => matched
RewriteCond %{REQUEST_FILENAME} !\.gz$ は、最初から .gz ファイルをアクセスされた場合は Rewrite しないための判定。これをやらないと、file.html.gz にアクセスがあった場合に file.html.gz.gz を探しに行ってしまう。もっとも、次の RewriteCond %{REQUEST_FILENAME}\.gz -s での gz ファイル存在チェックで弾かれるから、やらなくてもとりあえずは動く。あ、でもこれだと png や .jpg もチェック対象になるよなあ。やっぱり .html や .xml だけを対象として明示した方がいいかなあ。
RewriteCond %{REQUEST_FILENAME}\.gz -s は、.gz ファイルが存在するかどうかをチェックしている。.gz ファイルが存在しない場合は Rewrite せずにもとのリクエストのファイルを返す。
- HTTP トランザクションの中身
ブラウザでリクエストした結果。http://sonic64.com/cl.xml
GET /cl.xml HTTP/1.1
Host: sonic64.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja-JP; rv:1.7.10) Gecko/20050717 Firefox/1.0.6
Accept: text/xml,application/xml,application/xhtml+xml, text/html;q=0.9,text/plain;q=0.8, image/png,*/*;q=0.5
Accept-Language: ja,en-us;q=0.7,en;q=0.3
Accept-Encoding: gzip,deflate
Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
HTTP/1.x 200 OK
Date: Wed, 14 Sep 2005 22:51:21 GMT
Server: Apache/1.3.33 (Unix)
Last-Modified: Tue, 13 Sep 2005 23:08:14 GMT
Etag: "3391fb-2ecc-43275bde"
Accept-Ranges: bytes
Content-Length: 11980
Keep-Alive: timeout=3, max=8
Connection: Keep-Alive
Content-Type: application/xml
Content-Encoding: gzip
RewriteLog の出力内容。
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (3) [per-dir /home/hiroaki/public_html/test/] strip per-dir prefix: /home/hiroaki/public_html/test/cl.xml -> cl.xml
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (3) [per-dir /home/hiroaki/public_html/test/] applying pattern '.+' to uri 'cl.xml'
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (4) RewriteCond: input='gzip' pattern='gzip' => matched
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (4) RewriteCond: input='/home/hiroaki/public_html/test/cl.xml' pattern='!\.gz$' => matched
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (4) RewriteCond: input='/home/hiroaki/public_html/test/cl.xml.gz' pattern='-s' => matched
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (2) [per-dir /home/hiroaki/public_html/test/] rewrite cl.xml -> /~hiroaki/test/cl.xml.gz
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#80941ac/initial] (1) [per-dir /home/hiroaki/public_html/test/] internal redirect with /~hiroaki/test/cl.xml.gz [INTERNAL REDIRECT]
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#8095f1c/initial/redir#1] (3) [per-dir /home/hiroaki/public_html/test/] strip per-dir prefix: /home/hiroaki/public_html/test/cl.xml.gz -> cl.xml.gz
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#8095f1c/initial/redir#1] (3) [per-dir /home/hiroaki/public_html/test/] applying pattern '.+' to uri 'cl.xml.gz'
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#8095f1c/initial/redir#1] (4) RewriteCond: input='gzip' pattern='gzip' => matched
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#8095f1c/initial/redir#1] (4) RewriteCond: input='/home/hiroaki/public_html/test/cl.xml.gz' pattern='!\.gz$' => not-matched
10.9.7.2 - - [14/Sep/2005:23:35:52 +0900] [192.168.0.38/sid#804b97c][rid#8095f1c/initial/redir#1] (1) [per-dir /home/hiroaki/public_html/test/] pass through /home/hiroaki/public_html/test/cl.xml.gz
- Vary ヘッダを出力した方がより明示的
HTTP レスポンスヘッダに Vary: Accept-Encoding を追加するとより理想的な動きになる。Vary ヘッダは 2004-02-06 「http の Vary レスポンスヘッダの意味と使用例」で書いた。mod_rewrite で任意の HTTP レスポンスヘッダを生成させることってできるのかな? 環境変数の値を設定することはできるけど、直接ヘッダに追加する方法は見つけられなかった。あ、HEADER ディレクティブを使えばいいのか。以下のような感じかな?
Header append Vary Accept-Encoding
ただ、これだといつも Vary を送ってしまうので、レスポンスに Content-Encoding: gzip があるときだけ Vary を返すようにしたいけど、環境変数の値に応じて条件分岐できるのは Apache 2.0 系だけ? ドキュメントを読んでみる。
mod_headers - Apache HTTP サーバ Header ディレクティブ
http://httpd.apache.org/docs/2.1/mod/mod_headers.html#header
構文: Header [condition] set|append|add|unset|echo header [value] [early|env=[!]variable]
Apache module mod_headers
http://httpd.apache.org/docs/1.3/mod/mod_headers.html
Syntax: Header set|append|add header value
1.3 系のドキュメントには env= という構文が使えるとは書いてないもんなあ。どちらにせよ、mod_header のモジュールがステータス: Extension じゃあ使えない可能性が高い。
そもそも Vary ヘッダの付加って 必須? 推奨? HTTP1.1 を規定している RFC 2616 ではどうなってる?
ハイパーテキスト転送プロトコル -- HTTP/1.1
http://www.studyinghttp.net/cgi-bin/rfc.cgi?2616#Sec14.44
HTTP/1.1 サーバは、サーバ駆動型ネゴシエーションを受けるあらゆるキャッシュ可能なレスポンスに Vary ヘッダフィールド値を含むべきである。そうする事で、キャッシュはそのリソースへの将来のリクエストを適切に解釈する事ができ、ユーザエージェントにそのリソースへのネゴシエーションの存在について知らせる事ができる。サーバは、サーバ駆動型ネゴシエーションを受けるキャッシュ不可能なレスポンスにも、ユーザエージェントにそのレスポンス時には変化してしまうレスポンスのについての有益な情報を提供するであろうから、Vary ヘッダフィールド値を含む事ができる。
「含まなければならない」ではなく、「含むべきである」なので必須ではないということか。じゃあ、とりあえずは Vary なしでいいか。
追記。
Internet Explorer がクライアントとき、HTTP レスポンスで Vary ヘッダを返してしまうと、IE は HTTP リクエストヘッダ If-Modified-Since を送信してこないという問題があるようだ。その結果、あらゆるレスポンスを 200 OK で返さざるを得なくなり、304 Not Modified によるレスポンスを返せなくなる。Vary を送らなければこの問題は回避できるようなので、Content-Encoding しか返さない方が総合的に見れば問題が少ないかも。
以上の内容はhttp://sonic64.com//2005-09-15.htmlより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます
モバイルやる夫Viewer Ver0.14