2013-01-25

HTTP リソースをキャッシュ可能にする

サーブする動的コンテンツをキャッシュ可能にさせるためにはサーバはどう動けばいいのか調べた.

Last-Modified

ウェブサイトが動的なコンテンツの場合Last-Modifiedは無視される傾向にあります。むしろ、Last-Modifiedをきちんと送ってくるケースは非常に稀です (Last-Modifiedについて | Torisugariの日記 | スラッシュドット・ジャパン).

ハイパーテキスト転送プロトコル -- HTTP/1.1 (RFC2616) を見る.キャッシュサーバがもっているキャッシュエントリとオリジンサーバが返すものが同じとき,キャッシュサーバはキャッシュエントリをクライアントに返す (13.1.1 キャッシュの正当性). その判断は,Last-modified の時刻と ETag ヘッダ値による (13.3.1 Last-modified の日付,13.3.2 エンティティタグのキャッシュバリディタ).

オリジンサーバにとってより望まれる動作とは強いエンティティタグと Last-Modified 値の両方を送る事である (「強いエンティティタグ」とは ETag ヘッダ値を指す;13.3.4 エンティティタグや Last-Modified の日付を使う場合の規定). しかしここでは ETag ヘッダ値についてはこれ以上触れないことにする.キャッシュサーバを含むクライアントは,Last-Modified 値のみがオリジンサーバによって提供されていたら、非サブレンジキャッシュ条件付きリクエスト内で (If-Modified-Since を使って) この値を使うべきである (13.3.4)

(この段落にはあまり自信がない) ある種の動的コンテンツについて ETag ヘッダ値を返すのは不適切と思う.強いエンティティタグは関連するエンティティ値にどんな点においての変化があっても変わらなければならない (13.3.4) ので,例えばあるデータを表形式で示す動的コンテンツにおいて,状況によって行の順序が変化するばあいデータが変わらなくても異なる ETag ヘッダ値を返さねばならないが,これは無駄と思う.

Last-modified 時刻による判断には次の制限がある.エンティティの更新時間は、リソースを1秒以内に2度更新する事は可能なので、もしそれが秒解像度に相当するなら弱いバリディタである (13.3.3 弱いバリディタと強いバリディタ).このため,キャッシュエントリが、オリジンサーバが元々のレスポンスを送った時の時刻を与える Date 値を含み、与えられた Last-Modified 時刻は、その Date 値から最低 60 秒前である (13.3.3) ことが必要とされる.オリジンサーバはこれに合うように Last-modified 時刻を返さなければならない.

Last-Modified 時刻 (例えば If-Modified-Since や If-Unmodified-Since 各ヘッダフィールド内で) と一つ以上のエンティティタグ (例えば If-Match, If-None-Match, If-Range 各ヘッダフィールド内で) をキャッシュバリディタとして含む条件付きリクエストを受信した HTTP/1.1 オリジンサーバは、その行いがそのリクエスト内の条件付きヘッダフィールドのすべてに一致しているのでなければ 304 (Not Modified) ステータスを返してはならない (13.3.4). ここで扱っているサーバは ETag ヘッダ値を返してはいないのだから,If-Match, If-None-Match, If-Range ヘッダを含まずに If-Modified-SinceIf-Unmodified-Since ヘッダを含む要求に対して 304 (not modified)412 (Precondition Failed) を正しく返せばいいことになる (14.25 If-Modified-Since,14.28 If-Unmodified-Since).

Cache-Control

Cache-Control 一般ヘッダフィールドは、リクエスト/レスポンス連鎖上のすべてのキャッシングメカニズムが従わなければならない指示を記述するために使用される (14.9 Cache-Control).

オリジンサーバは,キャッシュ可能でないコンテンツには private あるいは no-cache 指示子を付して返答しなければならない (14.9.1 キャッシュ可能とは何か).private は例えばユーザ認証を経て配布したコンテンツに対して付す.no-cacheno-store 指示子 (14.9.2 キャッシュによって保存できるものは何か) については,例えば caching - Why both no-cache and no-store should be used in HTTP response? - Stack Overflow にあるように理解し辛い.web browser による動作の違いもあるようだ.また,HTTP — The Varnish Book によれば no-store は store-but-do-no-serve-from-cache-without-revalidation の意とある.

max-ages-maxage 指示子,あるいは Expires ヘッダによってコンテンツの有効期限を示すことができる (14.9.3 基本的な期限のメカニズムの修正).ここでは示さないことにして,これ以上触れない.

must-revalidateproxy-revalidate 指示子は 例えば黙って履行されていない財務的処理のように、エンティティの再検証が失敗する事でリクエストが不適当な操作というような結果になる場合にのみ、サーバは must-revalidate 指示子を送るべきである (14.9.4).ここではこれ以上触れない.

no-transform 指示子によって Content-EncodingContent-RangeContent-Type ヘッダを変更してはならない.これらのヘッダによって指定されたエンティティボディの、エンティティボディ自身の値を含め、いかなる部分も変更してはならない 事を意味する (14.9.5 no-transform 指示子;13.5.2 修正できないヘッダ).

Vary

Vary フィールド値は、そのレスポンスが新鮮である{fresh} 間、キャッシュが再検証無しにそれに続くリクエストに対するレスポンスとして使ってよいかどうかを、完全に決定するためのリクエストヘッダフィールドのセットを示す (14.44 Vary).例えば,The most common usage of Vary is to use Vary: Accept-Encoding, which tells caches (Varnish included) that the content might look different depending on the Accept-Encoding-header the client sends. In other words: The page can be delivered compressed or uncompressed depending on the client (HTTP — The Varnish Book).