HTTPキャッシュを使いこなして、Webアプリを快適に(2)
2023年05月25日 木曜日
CONTENTS
「HTTPキャッシュを使いこなして、Webアプリを快適に(1)」の後半です。
まだ読んでない方は、そちらを先に読んでください。
cats_dogs開発者のヒラマツです。
今回は、HTTPのキャッシュ制御の応用的な仕組みの話になります。
HTTPキャッシュの検証
HTTPには、サーバのデータとキャッシュを比較する動作が定義されています。
この動作をHTTPキャッシュ用語で「検証(validation)」といい、この検証を利用すると、更新を反映しつつ、キャッシュを長く再利用できるようになります。
検証ありのHTTPリクエストを送信すると検証が行われますが、その動きは以下の図になります。
データが更新されてなければ、応答のデータが省略され、キャッシが再利用されます。 データが更新されていれば、応答で新しいデータが送信されます。
そして、データの更新されてないときの応答(図の3bの「無更新の応答」)に使われるのが、HTTPの「304 Not Modified 」の応答です。
「304 Not Modified 」に対応すると、ブラウザの処理方法は以下のように変わります。
条件 | 処理内容 |
---|---|
更新有り(3a) | 通常の処理と同様に、更新された新しいデータが送られてくるので、新しいデータを表示に使う。 |
更新無し(3b) | 「304 Not Modified 」の応答では、データは送られてこないので、既存のキャッシュを再利用する。同時に利用したキャッシュのキャッシュ期限を再計算して延長する。 |
「304 Not Modified 」に対応すると、同じデータを送らなくなるので、通信量が削減できます。
参照を3回繰り返した場合の例を、検証ありの「304 Not Modified ステータス」を使った動きに書き換えたのが、以下の図です。
通信回数は減っていませんが、データの送信は最初の1度だけで、通信量が大きく減っています。 このように、「304 Not Modified 」をうまく使えば、短いキャッシュ期間の問題を軽減しつつ、通信コストや処理時間も節約できるのです。
Webアプリ側の「304 Not Modified」の処理
とても都合が良さそうな「304 Not Modified 」ですが、ブラウザだけが対応してもだめで、WebサーバおよびWebアプリが対応しないと機能しません。 WebサーバおよびWebアプリが「304 Not Modified」に対応する場合、具体的には、以下の2つの方法のどちらかで対応することになります。
- コンテンツ(ファイル)の更新時間の新しさで判断する(
If-Modified-Since
ヘッダー利用) - コンテンツ(ファイル)の識別子の一致で判断する(
If-None-Match
ヘッダー利用)
この2種類の方法について、順番に説明して行きます。
コンテンツ更新時間で「304 Not Modified」
コンテンツ(ファイル)の更新時間の新しさで判断する場合は、以下のような処理になります。
対象 | 動作概要 |
---|---|
Webサーバ(Webアプリ) | コンテンツ更新時間を応答にLast-Modified ヘッダーに付けて応答する。もし要求に If-Modified-Since ヘッダーがあり、コンテンツが新しくなければ「304 Not Modified ステータス」の応答をする。 |
ブラウザ | キャッシュしてるデータのLast-Modified ヘッダーをIf-Modified-Since ヘッダーにつけて、要求を送信する。 |
要は、更新時間(Last-Modified
ヘッダーとIf-Modified-Since
ヘッダー)の比較で、更新の有無を判断します。
ただし、WebサーバおよびWebアプリが対応していないと、If-Modified-Since
ヘッダーは無視されるので、普通のコンテンツ要求として処理されます。この場合、データが無条件に送信されるため、キャッシュは再利用されません。
コンテンツ識別子で「304 Not Modified」
もう1つのコンテンツ(ファイル)の識別子の一致で判断する場合は、以下のような処理になります。
対象 | 動作概要 |
---|---|
Webサーバ(Webアプリ) | 生成するコンテンツの識別子をETag ヘッダーとして送信する。もし、 If-None-Match ヘッダーがあれば、コンテンツのETagヘッダーの値と比べる。一致したときは「304 Not Modified ステータス」の応答をする。 一致しないときは、通常通り、コンテンツを含んだ応答をする。 |
ブラウザ | キャッシュしているデータのETag ヘッダーの値を、If-None-Match ヘッダーとして、送信する。 |
要は、コンテンツを識別する値(ETag
ヘッダーとIf-None-Match
ヘッダー)の比較で、更新を判断するのです。
ちなみに、If-Modified-Since
ヘッダーとIf-None-Match
ヘッダーが、同時に指定されている場合は、If-None-Match
ヘッダーの処理を優先します。
この方法の、メリットとデメリットは以下の通りです。
- メリット
Etag
の値で柔軟に一致条件を制御できる。- 更新時刻以外の情報を判断に使える。(例えば、Webサーバは、FSのファイルをそのまま見せる場合に、inode番号を使ってETagを生成したりする。)
- デメリット
Etag
の値を適切に生成しなければならない。
柔軟さの例えとしては、広告部分だけが異なるページを、キャッシュ的には同じページとして扱うようなことが可能になります。(広告違いのデータをダウンロードする無駄が防げたりします。)
If-Modified-Since
ヘッダーと同様に、WebサーバおよびWebアプリが対応していないと、If-None-Match
ヘッダーは無視されるので、普通のコンテンツ要求として処理されます。この場合も、データが無条件に送信されるため、キャッシュは再利用されません。
同じURLの異なるWebページ
キャッシュの再利用は、通常、同じURLのページに対して行われます。 通常は、ページごとにURLが異なるので、問題になりません。
しかし、1つのURLで異なるWebページを提供する事があります。 例えば、ブラウザの言語設定やブラウザ種別で、表示を変えるような場合です。
このような場合、キャッシュ制御を間違えると、日本語モードで英語ページが表示されるようなページの入れ替わりが起こります。
このような場合には、追加の識別情報を提供して、正しくページが識別されるようにします。
その方法がVary
ヘッダーです。
具体的には、Webサーバは識別に利用するHTTPヘッダー名をVary
ヘッダーへ設定することで、ブラウザにWebページの識別方法を教えます。 そして、ブラウザは、Vary
ヘッダーで指定されたヘッダー名のヘッダー内容と、URLを組み合わせて、Webページを区別します。
ですので、例えば、Vary: User-Agent
やVary: Accept-Language
のような指定で、この問題を回避するのです。
プロキシのキャッシュ
Webサーバとブラウザが直接やり取り場合については、おおよそ説明できました。 ただ、プロキシに関しては、あまり説明してませんので説明していきます。
プロキシを使ったときの動きは、プロキシがWebサーバとブラウザの間に入るので、以下の図のようになります。
プロキシは、ブラウザ側へはWebサーバとして動作し、Webサーバ側にはブラウザとして動作します。 要は、ブラウザとWebサーバを組み合わせた動きになります。
プロキシもブラウザ的な動きをするので、一時的な複製データを作ります。 なので、結果的にプロキシにもキャッシュが存在します。
以下の図のように、Webサーバと利用者の間には、プロキシのキャッシュと、ブラウザのキャッシュの、2つのキャッシュが存在することになります。
プロキシは、複数の利用者に使われることが多いので、実際は、キャッシュが以下の図のようなツリー状の構造になります。
もちろん、プロキシを複数組み合わせて、キャッシュの構造をより多段で複雑なものにするのも可能です。
プロキシとブラウザでの、キャッシュの違い
キャッシュのデータを一時的に複製する動作は、プロキシとブラウザで大きく変わりません。 しかし、ブラウザとプロキシのキャッシュは、区別して扱う必要があります。
その理由は、ブラウザとプロキシは、管理している人が違うからです。
ブラウザは大抵、利用者本人が管理しているわけですが、プロキシは、大抵第三者が管理しています。
ということは、Webサーバのコンテンツ所有者、経路途中にあるプロキシ管理者、ブラウザ利用者の3つの立場を考慮して、キャッシュ制御を考える必要があるわけです。 例えば、クレジットカードなどの決済情報は、本人以外に漏洩しては困るので、プロキシではキャッシュを再利用しないよう制御すべきです。
この様に、仕組みは同じでも管理者が違うことから、ブラウザとプロキシで、キャッシュの扱いを変えなければならないのです。
つづく…
途中で、Cache-Control
ヘッダーの話が全く出てこないので、おかしいと気がついた方は正解です。
HTTPのキャッシュ制御の説明は、まだ終わりません。
肝心のCache-Control
ヘッダーについては続編の「続・HTTPキャッシュを使いこなして、Webアプリを快適に」で説明します。
折角ここまで読んだわけですし、ぜひ読んでください。