この記事はnginx Advent Calendar 2015の3日目の記事です。
コンテンツキャッシュ
HTTP通信時にクライアント/サーバ間にキャッシュサーバが存在する場合、キャッシュサーバは特定のヘッダの内容ごとにキャッシュを分けるといった工夫が必要になることがあります。
クライアント <-> キャッシュサーバ <-> サーバ
例えばクライアントがAccept-Encoding: gzip, deflate
付きのリクエストを送ってきた場合、サーバはgzip圧縮したコンテンツを返すことができます。さらに前段のキャッシュサーバはそのレスポンスをキャッシュすることができます。
しかし、すべてのクライアントがAccept-Encoding: xxx
付きのリクエストを送ってくるとは限りません。この場合、サーバは素のコンテンツをレスポンスとして返します。また、前段のキャッシュサーバはこのレスポンスもキャッシュすることができます。
さて、ここで問題が生じます。キャッシュサーバは圧縮済みのコンテンツをキャッシュすべきでしょうか?それとも圧縮していないコンテンツをキャッシュすべきでしょうか?
この問題はVaryヘッダを利用することで解決できます。
Varyヘッダ
Varyヘッダは前段のキャッシュサーバに対して、指定したヘッダの内容ごとにキャッシュを分ける必要があることを伝えるためのものです。例えばサーバがVary: Accept-Encoding
をレスポンスヘッダに付加しておくと、キャッシュサーバはAccept-Encodingヘッダの内容に応じたキャッシュを保持します。
Vary: Accept-Encoding
こうすることでクライアントのAccept-Encodingヘッダの内容に応じたキャッシュデータをキャッシュサーバは返すことができるというわけです。
nginxにおけるgzip圧縮とVaryヘッダ
さて、本題です。上記のような事情からかApacheのmod_deflateやh2oなんかはコンテンツのgzip圧縮を有効にすると、自動的にVary: Accept-Encoding
をレスポンスヘッダに付加します。一方我らがnginxは設定ファイルにgzip_vary on;
と書かないとVary: Accept-Encoding
をレスポンスヘッダに付加しません。
えるしってるか、Apacheのmod_deflateはレスポンスヘッダに「Vary: Accept-Encoding」をデフォルトで付ける。しかしnginxは「gzip_vary on;」の一行を追加しない限り何もしない。
— bokko (@cubicdaiya) November 14, 2015
非常に重要なことなのでもう一度言うと、nginxは設定ファイルにgzip_vary on;
と書かないとVary: Accept-Encoding
をレスポンスヘッダに付加しません。
gzip_vary on;
nginxの前段にキャッシュサーバやCDNが配置されている際はこの事実を頭に入れておくとよいでしょう。
(おまけ)nginxとViaヘッダ
ヘッダの内容ごとにキャッシュを分ける件はこれでよいとして、nginxの前段にキャッシュサーバやCDNがあるともうひとつめんどくさいのがあります。Viaヘッダです。
Viaヘッダはクライアント/サーバ間にあるキャッシュサーバやCDNが、「自分のサーバをプロキシとして経由したよー」と明示するために付けるヘッダです。例えばCloudfrontなんかを通るとこのヘッダが付いてきます。そしてnginxは受け取ったリクエストにViaヘッダが含まれていると設定ファイルにgzip on;
と書いてもレスポンスをgzip圧縮しません。
えるしってるか、リクエストヘッダにViaヘッダが付いてるとnginxは「gzip on;」って書いてもレスポンスをgzip圧縮しない。
— bokko (@cubicdaiya) November 14, 2015
非常に重要なことなのでもう一度言うと、nginxは受け取ったリクエストにViaヘッダが含まれていると設定ファイルにgzip on;
と書いてもレスポンスをgzip圧縮しません。
Viaヘッダが付いててもレスポンスをgzip圧縮したい場合はgzip_proxied
ディレクティブを利用します。
gzip_proxied any;
これでViaヘッダ付きのリクエストに対してもgzip圧縮したレスポンスを返すことができます。めでたしめでたし。