Studying HTTP

Help

HTTP Caching

この頁では、複雑なHTTP/1.1キャッシングについて、特にキャッシュ期限モデルキャッシュ検証モデルについてを解説します。

ナウでヤングなレンタルサーバー!ロリポップ!

キャッシュとは

プロクシ等の中間プログラムを使うと、その分交信回数が増える事になります。 それだけ手間がかかるわけですから、そこに何かしらのメリットが無ければ、この交信は無駄になってしまいます。

プロクシを使うメリットとしては、RFC 2616 の section 1.3 にある通り、ユーザエージェントに、ある種の翻訳、メディアタイプの変形、使用プロトコルの制限、匿名性向上のためのフィルタリング等のような各サービスを提供する 事が挙げられますが、この他にプロクシの代表的な使われ方としてキャッシュ (cache) があります。 レスポンスをキャッシュする事によって、サーバのパフォーマンスは大幅に向上するでしょう。

例えば、"今見ているリソースの1つ前に見たページに戻る場合"を考えてみましょう。 この時、そのリソースがキャッシュ可能で、かつキャッシュとして保存されていれば、すぐにそれを利用する事が可能です。 しかし、キャッシュされていなければ、再度サーバにリクエストをし、リソースを取り寄せなければいけません。 つまり、キャッシュを利用する事によって同じデータを何度もサーバから転送させる必要が無くなり、時間的・金銭的通信コストが節約され、またサーバの負荷も軽減できるという事になります。

キャッシュ、及びキャッシュに関する専門用語については section 1.3 の中で定義されています。 RFC 2616 では、キャッシュの仕様をリソースの状態に矛盾が無いように細かく規定していますが、この中にはキャッシングを解説するにあたり頻繁に使われる専門用語がありますので、これを読むためにはそれらの用語の意味を確実に理解していなければいけません。

キャッシュ {cache}
プログラムがレスポンスメッセージをローカルに記録しておく場所であり、それらメッセージの保存、検索、削除を管理するサブシステムを指す。 キャッシュは、同様のリクエストが起きた際にレスポンス時間やネットワーク帯域幅の消費の軽減のために、キャッシュ可能なレスポンスを保存する。 どのようなクライアントもサーバもキャッシュを持つ事は出来るが、トンネルとして振る舞っているサーバはキャッシュを使用できない。
キャッシュ可能 {cacheable}
レスポンスは、もしキャッシュが後続のリクエストに答えるという目的での使用のために、レスポンスメッセージのコピーを保存する事が許されるならばキャッシュ可能であるという。 HTTP レスポンスのキャッシュ使用の決定の決まりは section 13にて定義されている。 例えリソースがキャッシュ可能でも、キャッシュは特定のリクエストのためにキャッシュされたコピーを使うかどうかについて制限を受ける可能性がある。
ファーストハンド {first-hand}
レスポンスが、オリジンサーバ、あるいは一つ以上経由したプロクシから不必要な遅れ無しに届くならば、それをファーストハンドであるという。 また、オリジンサーバで有効性を直接チェックされたレスポンスもファーストハンドであるという。
明示的有効期限 {explicit expiration time}
オリジンサーバが、エンティティの有効性の再確認無しにキャッシュを返すべきではないとしている時刻。
帰納的有効期限 {heuristic expiration time}
有効期限が指定されていない時に、キャッシュによって指定される有効期限。
経過時間 {age}
レスポンスの経過時間とは、それがオリジンサーバから送られてから、あるいはオリジンサーバによって十分に有効性が確認された時からの時間を指す。
有効期間 {freshness lifetime}
レスポンスが生成されてから有効期限までの時間の長さ。
新鮮である {fresh}
レスポンスは、その経過時間が有効期間を経過していないものを新鮮であるという。
新鮮でない {stale}
レスポンスは、その経過時間が有効期間を経過したものを新鮮でないという。
意味的に透過である {semantically transparent}
キャッシュは、パフォーマンスの向上以外に、リクエストしたクライアントにもオリジンサーバにも影響が無い時、特定のレスポンス関して、意味的に透過な方法で振る舞う。 キャッシュが意味的に透過な時、クライアントはオリジンサーバから直接処理された時に受け取るであろうリクエストと全く同じレスポンスを受け取る。 (ホップバイホップヘッダを除く)
バリディタ {validator}
キャッシュ内にあるリソースが、エンティティのコピーと同等かどうかがわかるとされているプロトコルエレメント。(例えば、エンティティタグや最終更新時刻など)

このうち、理解し難い用語は意味的に透過バリディタの二つでしょう。

意味的に透過とは、結局キャッシュがある時と無い時でその振る舞いが変わらない、すなわちクライアントやオリジンサーバからはまるでキャッシュが存在しないかのように見える時、そのキャッシュは意味的に透過と言う事ができます。

またバリディタとは、日本語に訳すとすれば正当性検証子であり、そのリソースの正当性、有効性を検証するためのものと言えるでしょう。

キャッシュはユーザエージェント単位でも利用されていますが、これを複数のコンピュータ間で共有するために使われるのがプロクシです。 プロクシがキャッシュとして働く場合の振る舞いについては、section 1.4 の中で記されています。

トンネルとして動作していない通信のすべてのパーティは、内部的なキャッシュやリクエスト処理に使用できる。 キャッシュの効果とは、もし連鎖上に連なるある一つがそのリクエストに適用できるキャッシュされたレスポンスを持っているなら、リクエスト/レスポンス連鎖を短縮する事である。 以下では、もし B が、UA や A がキャッシュしていないリクエストに対する O からの (C を経由した) 以前のレスポンスのキャッシュされたコピーを持っている場合の結果となる連鎖を説明している。

    request chain ---------->
 UA -----v----- A -----v----- B - - - - - - C - - - - - - O
    <--------- response chain

すべてのレスポンスがキャッシュ可能であるわけではなく、いくつかのリクエストではキャッシュの動作への特別な要求を行う修飾子を含む事ができる。 キャッシュの動作やキャッシュ可能なレスポンスに対する HTTP の要求は section 13 で定義されている。

プロクシのキャッシングは、何度も利用されるリソース程、効果が大きく現れます。 但し、CGI 等によって動的に生成されるリソースや、ニュースサイトのようなリアルタイム性が要求されるサイトのリソースは、リソースがキャッシュ可能で無いかもしれませんし、またもしキャッシュ可能であっても、それがキャッシュに残っているせいで最新のリソースを利用する事ができないなどの不都合が起こる可能性があるので、キャッシュの利用者や管理者は注意が必要です。

キャッシュの正当性

"正当なキャッシュ"、つまりキャッシュとして使えるものを決定するためのメカニズムについて、section 13.1.1 に示されています。

正当なキャッシュは、以下の状況の一つに合うリクエスト (section 13.2.5, 13.2.6, 13.12 参照) に適した、キャッシュが持っている最新のレスポンスをもってリクエストに答えなければならない

  1. オリジンサーバがレスポンスの再検証によってオリジンサーバで返されたものとの等価性が確認されている (section 13.3)。

  2. "十分に新鮮" (section 13.2 参照) である。 既定の場合、これはクライアント、オリジンサーバ、キャッシュの最低限の新鮮度要求に合う事を意味する (section 14.9 参照)。 オリジンサーバがそう指定する場合、それはオリジンサーバ単独の新鮮度要求である。

    保存されたレスポンスがクライアントとオリジンサーバの両方の最も限定的な新鮮度要求から "十分に新鮮" で無い場合、そのようなレスポンスが禁じられている (例えば "no-store" キャッシュ指示子や、"no-cache" キャッシュリクエスト指示子等;section 14.9 参照) ので無ければ、深く考慮された環境ではキャッシュが適切な Warning ヘッダを伴ってそのレスポンスを返してもよい

  3. 適切な 304 (Not Modified), 305 (Proxy Redirect) かエラー (4xx5xx) レスポンスメッセージである。

キャッシュがオリジンサーバと通信できない時、レスポンスがキャッシュから正しく対応させる事ができるならば、正しいキャッシュは上記のものを返すべきであり、そうでなければ通信失敗があった事を示すエラーか警告を返さなければならない

つまり、あるデータがキャッシュとして使える場合には次の二通りあります。

  1. オリジンサーバから取得してから、十分に新鮮であるもの
  2. オリジンサーバに有効なリソースが存在する場合に、それがオリジンサーバにあるものと同一であると検証済みのもの

前者を期限{Expiration} モデル、後者を検証{Validation} モデルと言います。

キャッシュ期限モデル

あまりに古くなった情報はその価値が失われます。 従って、十分に新鮮であるもののみをキャッシュとして使うというものがキャッシュ期限モデルであり、section 13.2 に記述されています。

HTTP キャッシングは、キャッシュがオリジンサーバにリクエストを送るという事を完全に避ける事ができる時に最善に動作する。 リクエストを避けるための第一のメカニズムは、レスポンスが以降のリクエストを満足させるために使用する事ができるという事を含め、オリジンサーバが将来において明確な期限切れになる時間を提供する事である。 言い換えれば、キャッシュはサーバに先に接続する前に新鮮なレスポンスを返す事ができる。

我々が期待する事は、有効期限が切れる前にエンティティは意味的に重要な方法では変更無いであろう、という確信を持って、サーバがレスポンスに将来の明確な有効期限を割り当てているだろう事である。 これは普通、サーバが期限を注意深く選んでいる間は、意味的な透過性を維持する。

期限メカニズムは、キャッシュから取得したレスポンスのみに適用され、リクエストしているクライアントに直ちに転送されたファーストハンドのレスポンスには適用されない。

期限メカニズムとは、オリジンサーバがキャッシュの有効期限を定め、それを越えたキャッシュはオリジンサーバにそのキャッシュとしての正当性・有効性を確認しない限りはキャッシュとして使えないというものです。

有効期限には、オリジンサーバによって明示的に示される明示的有効期限の他に、帰納的有効期限があります。 帰納的有効期限について、13.2.2 をご覧下さい。

オリジンサーバは明示的有効期限を常に提供するわけではないので、HTTP キャッシュは典型的に、本当の有効期限を見積もるために (Last-Modified の時間のような) 別のヘッダ値を使うようなアルゴリズムを使って、帰納的有効期限を割り当てる。 HTTP/1.1 仕様書では詳細なアルゴリズムを提供しないが、その結果への最悪の場合の制約を課す。 帰納的有効期限は意味的な透過性を損なうかもしれないので、それらは慎重に使用されるべきであり、我々はオリジンサーバが可能な限り明確な有効期限を提供するという事を推奨する。

帰納的有効期限とはキャッシュによる自主的な推測の有効期限なので、これを使用した場合、例えばオリジンサーバのリソースは既に変化しているにもかかわらず、キャッシュは古いリソースをクライアントに返してしまうかもしれません。 従って、オリジンサーバはできるだけ明示的有効期限を示すべきだし、キャッシュも帰納的有効期限の使用には慎重にならなければいけません。

キャッシュの有効期限を示すためには、HTTP ヘッダを使います。 HTTP/1.0 において、キャッシュを制御するための HTTP ヘッダは、PragmaExpires があります。 受信者は、Pragma: no-cache というヘッダを受信した場合、そのレスポンスをキャッシュしてはいけません。 また Expires の値は、リソースが古くなったとみなす HTTP 日付で、リソースの有効期限を表しています。

HTTP/1.1 ではこれらの他に、より細かくキャッシュ制御できるように Cache-Control ヘッダが新設されました。 Cache-Control については、section 14.9 に定義されています。

Cache-Control 一般ヘッダフィールドは、リクエスト/レスポンス連鎖上のすべてのキャッシングメカニズムが従わなければならない指示を記述するために使用される。 キャッシュがリクエストやレスポンスに不利になるように干渉させないような振る舞いを指定する。 これらの指示は、常に既定のキャッシングアルゴリズムを上書きするものである。 キャッシュ指示は、リクエスト中にある指示があっても、それと同じ指示がレスポンスで与えられるという事を暗に意味しない、という意味において単向性である。

HTTP/1.0 キャッシュは、Cache-Control を実装してせず、Pragma: no-cache (section 14.32 参照) しか実装していないかもしれない事に注意せよ。

キャッシュ指示は、リクエスト/レスポンス連鎖上のすべての受信者に適用できるため、プロキシやゲートウェイアプリケーションを、そのアプリケーションの重要性に関係なく通り抜けなければならない。 特定のキャッシュのために cache-directive を記述するのは不可能である。

 Cache-Control   = "Cache-Control" ":" 1#cache-directive

 cache-directive = cache-request-directive
      | cache-response-directive

 cache-request-directive =
        "no-cache"                          ; Section 14.9.1
      | "no-store"                          ; Section 14.9.2
      | "max-age" "=" delta-seconds         ; Section 14.9.3, 14.9.4
      | "max-stale" [ "=" delta-seconds ]   ; Section 14.9.3
      | "min-fresh" "=" delta-seconds       ; Section 14.9.3
      | "no-transform"                      ; Section 14.9.5
      | "only-if-cached"                    ; Section 14.9.4
      | cache-extension                     ; Section 14.9.6

  cache-response-directive =
        "public"                               ; Section 14.9.1
      | "private" [ "=" <"> 1#field-name <"> ] ; Section 14.9.1
      | "no-cache" [ "=" <"> 1#field-name <"> ]; Section 14.9.1
      | "no-store"                             ; Section 14.9.2
      | "no-transform"                         ; Section 14.9.5
      | "must-revalidate"                      ; Section 14.9.4
      | "proxy-revalidate"                     ; Section 14.9.4
      | "max-age" "=" delta-seconds            ; Section 14.9.3
      | "s-maxage" "=" delta-seconds           ; Section 14.9.3
      | cache-extension                        ; Section 14.9.6

 cache-extension = token [ "=" ( token | quoted-string ) ]

ある指示子がどんな 1#field-name パラメータも無く現れた時、その指令はリクエストやレスポンス全体に適用される。 ある指示子が 1#field-name パラメータを伴って現れたら、指定されたフィールド (群) のみに適用され、リクエストやレスポンスの残りの部分には適用されない。 このメカニズムは拡張性を持っている。 HTTP プロトコルの将来のバージョンの実装は、これらの指示子に HTTP/1.1 で定義されていないヘッダフィールドを適用する事ができる。

cache-control 指示子は、これらの一般的なカテゴリの中に分類する事ができる。

Cache-Control はリクエストにもレスポンスに使われます。 Cache-Control の指示子は、通常リクエスト/レスポンス連鎖全体に対して有効ですが、指定の仕方によっては接続単位の制御も可能であり、すなわち "クライアントがキャッシングサーバに"、"オリジンサーバがキャッシングサーバに"、あるいは "キャッシングサーバがクライアントに" 等と、様々な場面でキャッシュの振る舞いを制御する事ができるようになります。

HTTP/1.1キャッシュはHTTP/1.0キャッシュに優先します。 従って、Cache-ControlExpires に優先します。

キャッシュ検証モデル

キャッシュサーバが、クライアントからリクエストを受けたリソースについて、それをキャッシュとして使用できるかを判断する際に、それがオリジンサーバにあるものと同一であるかどうか検証し、これを満たすものをキャッシュとして使うのがキャッシュ検証モデルであり、section 13.3 に記述されています。

キャッシュがクライアントのリクエストへのレスポンスとして使いたいような新鮮で無いエントリを持っている時、そのキャッシュされたエントリがまだ使用可能かどうかを確かめるために最初にオリジンサーバ (かあるいは新鮮なレスポンスを持っている中間キャッシュ) へチェックしなければならない。 我々はこれをキャッシュエントリの "検証{validating}" と呼ぶ。 もしキャッシュされたエントリで良ければ我々は完全なレスポンスの再転送によるオーバーヘッドを望まないし、キャッシュされたエンティティが適切で無ければ余分なラウンドトリップによるオーバーヘッドを望まないので、HTTP/1.1 プロトコルは条件付きメソッドの使用をサポートしている。

条件付きメソッドをサポートするための重要なプロトコルの機能は、"キャッシュバリディタ" に関するものである。 オリジンサーバが全体のレスポンスを生成する時、それにある種のバリディタを付けられ、キャッシュエントリと共に保存される。 クライアント (ユーザエージェントやプロクシキャッシュ) がキャッシュエントリに持つリソースに条件付きリクエストを作る時、リクエストに関するバリディタを追加する。

この時サーバはそのバリディタと現在のエンティティのバリディタを調べ、もしそれらが一致すれば (section 13.3.3 参照) 、エンティティボディを含まない特別なレスポンスコード (通常は 304 (Not Modified)) を返す。 そうで無ければ、完全なレスポンス (エンティティボディを含む) を返す。 従って、もしバリディタが一致すれば完全なレスポンスを転送する事は避けられるし、一致しなければ余分なラウンドトリップを避けられる。

キャッシュ検証モデルでは、キャッシュサーバがオリジンサーバに条件付きリクエストを行う事でリソースの検証を行います。 この時、もしリソースが変更されていれば、キャッシュを新しいものと入れ替えます。 この検証のために使うものがバリディタですが、バリディタに使われるものには Last-Modified ヘッダの値と、エンティティタグがあります。 エンティティタグに付いて、section 3.11 をご覧下さい。

エンティティタグは、同一の要求リソースからの二つ以上のエンティティを比較するために使用される。 HTTP/1.1 では、ETag (section 14.19), If-Match (section 14.24), If-None-Match (section 14.26), If-Range (section 14.27) 各ヘッダフィールドで、エンティティタグを使う。 それらがキャッシュバリディタとして、どのよう使われ、比較されるかの定義は、section 13.3.3 にある。 エンティティタグは、それ自体は読んでも意味のわからない{opaque} 引用符で括られた文字列から成り、weakness インジケータが前方に付く場合もある。

 entity-tag = [ weak ] opaque-tag
 weak       = "W/"
 opaque-tag = quoted-string

"strong entity tag" では、もしそれらがオクテット文字によって同等の場合にのみ、リソースの2つのエンティティが共有できる

"W/" プレフィクスによって示される "weak entity tag" では、エンティティが等価であり、意味論においてそれぞれ重要な変更がなく互いを代わりに使う事が出来る場合のみ、リソースの 2 つのエンティティが共有できるweak エンティティタグは、弱い比較の時のみ使用される。

エンティティタグは、特有のリソースと関連付けられた全てのエンティティの全てのバージョンの中で一意{unique} でなければならない。 与えられたエンティティタグの値は、異なる URI へのリクエストから得られたエンティティのために使う事ができる。 異なる URI へのリクエストから得られたエンティティに同じエンティティタグの値を使っているからといって、それらのエンティティの同等性を暗に意味するものではない。

バリディタには、強いバリディタ弱いバリディタの二種類があり、section 13.3.3 に記述されています。

オリジンサーバやキャッシュの両方でそれが同じエンティティを表すかどうかを決定するために二つのバリディタを比較するため、通常はエンティティ(エンティティボディかエンティティヘッダ) が何らかの理由で変わっていたら、それに関するバリディタも同様に変更しているだろうという事を期待できる。 これが真である場合、我々はこのバリディタを "強いバリディタ" と呼ぶ。

しかしながら、サーバがエンティティの意味的に重要な変更時にのみバリディタを変更し、重要でない側面にはバリディタを変更したがらない場合があるかもしれない。 リソースの変更時に常に変更されないバリディタは "弱いバリディタ" である。

エンティティタグは通常 "強いバリディタ" であるが、このプロトコルでは "弱い" エンティティタグを付けるためのメカニズムを提供している。 強いバリディタはエンティティが少しでも変更した時に変更されるもので、弱いバリディタはエンティティの意味が変更した時に変更される、と考える事ができる。 または、強いバリディタは特定のエンティティの識別子{identifier} の一部であり、弱いバリディタは意味的に等しいエンティティのセットの識別子の一部である、と考える事もできる。

(中略)

バリディタの "使用" は、クライアントがリクエストを生成し検証用ヘッダフィールドにバリディタを含む時、あるいはサーバが二つのバリディタを比較する時のどちらかである。

強いバリディタはいかなる状況にも使う事ができる。 弱いバリディタはエンティティの正確な等価性に頼らない状況においてのみ使う事ができる。 例えば、完全なエンティティの条件付き GET のためにどちらかの種類を使う事ができる。 しかし、別の方法ではクライアントが内部的に一致しないエンティティとなってしまうかもしれないので、サブレンジ回収のためには強いバリディタのみを使う事ができる。

クライアントは、単純な (非サブレンジ) GET リクエストの発行には弱いバリディタも強いバリディタも使う事ができる。 その他のリクエストの形式には弱いバリディタを使ってはならない

HTTP/1.1 では、バリディタをエンティティの比較に使いますが、これも強いバリディタと弱いバリディタによって、強い比較弱い比較があります。

HTTP/1.1 プロトコルがバリディタに定義している機能は比較のみである。 二つのバリディタ比較機能があり、これは比較状況が弱いバリディタの使用を許すかどうかに依存する。

強い比較機能
等しさを検討するため、両方のバリディタはあらゆる方法で同一でなければならなく、両方のバリディタは共に弱くない。
弱い比較機能
等しさを検討するため、両方のバリディタはあらゆる方法で同一でなければならない。 ただし、これらのどちらかもしくは両方は結果に影響する事なく "弱い" とマークされている。

明確に弱いと印されていなければエンティティタグは強い。 section 3.11 ではエンティティタグのための構文を与えている。

例えば、オリジンサーバ中のエンティティタグが "abcde" の時、キャッシュ中のリソースのエンティティタグが

となります。

同一性が肯定された場合、キャッシュは自身が保持するリソースを返します。 また、クライアントが条件付きリクエストを行っていた場合は、304 (Not Modified) を返し、オリジンサーバのリソースが変更されていない事を伝えます。

同一性が否定された場合、キャッシュはオリジンサーバからリソースを取得し、それをクライアントに返します。 その際、キャッシュはリソースを新しいものに入れ換え、今後再び同一性検証が行われる場合は、新しいキャッシュに対して行われなければいけません。

なお、再検証中にオリジンサーバが 5xx レスポンスを返した場合について、section 13.8 に記述されています。

もしキャッシュがエントリの正当性再検証を試みる一方で 5xx レスポンスを受信したら、リクエストしているクライアントにこのレスポンスを転送するか、あるいはまるでサーバが応答に失敗したかのように動作するかどちらかを行う事ができる。 後者の場合、キャッシュされたエントリが "must-revalidate" cache-control 指示子 (section 14.9 参照) を含んでいなければ、以前に受信したレスポンスを返す事ができる

キャッシュと履歴メカニズム

多くのユーザエージェントには履歴メカニズムがあり、section 13.13 に記述されています。

ユーザエージェントはしばしば、"戻る" ボタンと履歴リストのような履歴メカニズムを持っていて、セッションにおいて以前に回収したエンティティを再表示するために使う事ができる。

履歴メカニズムとキャッシュは異なる。特に履歴メカニズムはリソースの現在の状態の意味的に透過なビューを表示しようとはすべきではない。 むしろ、履歴メカニズムはリソースが回収された時刻を正確に表示するという意味を持つ。

既定では、有効期限は履歴メカニズムには適用されない。 もしそのエンティティがまだ保存していて、ユーザが特にエージェントを期限切れの履歴文書をリフレッシュするように設定していなければ、履歴メカニズムは例えエンティティの期限が切れていてもそれを表示すべきである

これは、履歴システムがユーザにビューが古くなっているかもしれないという事を知らせる事を禁止すると解釈されるものではない。

注: もし履歴表メカニズムがユーザが古くなったリソースを見る事を不必要に妨げるのであれば、サービスの著者が別の方法を望む場合に、HTTP の期限制御とキャッシュ制御を強制的に使わせないようにする傾向があるだろう。 サービスの著者は、ユーザが前もって回収したリソースを見るために (「戻る」のような) ナビゲーション制御を使う時にユーザがエラーメッセージや警告メッセージが見えない事を重要だと考えるかもしれない。 例え、時にそのようなリソースはキャッシュされるべきではなく、すぐに期限が切れるべきだとしても、不適当に機能している履歴メカニズムの影響のよって苦しまないためにユーザインターフェースはサービスの著者にキャッシングさせないようにするための別の方法 (例: "一度きりの" URL) に強制的に訴える事ができるように考慮されている。

Internet Explorer や Netscape には履歴メカニズムが実装されており、これを用いて過去に取得したリソースを表示する場合は、キャッシュを用いる事ができます。 この時、もしキャッシュの有効期限が切れていたとしても、そのキャッシュを使う事ができますが、「このキャッシュは有効期限切れである」という旨は表示してもいいし、しなくてもかまいません。 有効期限切れであるリソースを利用させたくないサービス著者は、ユニークなクエリを持つような URL の使用等の方法を用いる事ができます。

参照文献


Copyright © 1999-2010 H-Hash, All Rights Reserved. Valid HTML 4.01 Strict 正当なCSSです!