概要
HTTPのキャッシュについて色々調べてみたのでメモ的な意味も兼ねて書いた。
動機としては以下の本を読む上で先に事前知識を入れておきたかったなというのがあります。キャッシュだけで400ページ超え。とても楽しみです。
目次
- 概要
- 目次
- それぞれのヘッダーのもつ意味
- Last-ModifiedとIf-Modified-Since
- EtagヘッダーとIf-None-Match
- Cache-ControlもExpireもない場合はどうなるの
- ブラウザにキャッシュはさせたいけど都度確認はしてきて欲しいとき
- from service workerって何
- まとめ
それぞれのヘッダーのもつ意味
Expiresヘッダー
If modified Sinceも送られないため、304レスポンス負荷が発生することもない。
HTTPレスポンスにCatch-Controlのmax-ageディレクティブが含まれる場合は、Expiresは無視されます
Apacheでやるならこんな感じで設定を書くことで適用することができます。キャッシュ先はユーザのブラウザとなるのでキャッシュヒット時にはユーザとしてはサービスが取りうる最高のレイテンシでサービスを使うことができます。
<ifModule mod_expires.c> ExpiresActive On ExpiresByType image/png "access plus 1 months" ExpiresByType image/jpeg "access plus 1 months" ExpiresByType image/gif "access plus 1 months" ExpiresByType text/css "access plus 1 months" </ifModule>
注意点としては
Cache-Control
public
レスポンスが通常はキャッシュ可能でなくても、レスポンスをどのキャッシュにも格納することができます。
private
Webサーバから返されるコンテンツがただ一人のユーザのためのものであることを示す。このコンテンツは、複数のユーザが共有されるキャッシュに記録されるべきではないことを表している。
この設定はCDNの設定で以下の流出の事件で有名なやつですね。
no-cache
「キャッシュを使うな」のように見えるこのヘッダが実際に意味するところは少々ニュアンスが異なる。このヘッダの意味は、いちどキャッシュに記録されたコンテンツは、現在でも有効か否かを本来のWebサーバに問い合わせて確認がとれない限り再利用してはならない、という意味である。
no-store
このヘッダは、Webサーバから返されてくるコンテンツをキャッシュに記録するな、という指示である。
「no-cache」と「no-store」の違い
no-cacheは、同じURLに対する後続のリクエストへのレスポンスとして、以前返されたレスポンスを使用するには、まずサーバーに問い合わせてレスポンスに変更があったかどうかを確認する必要があることを示します。
no-storeはより単純で、返されたレスポンスのバージョンにかかわらず、ブラウザのキャッシュやすべての中間キャッシュはそのレスポンスを一切格納できません。たとえば、個人の機密データや銀行データが含まれているレスポンスなどです。ユーザーがこのアセットをリクエストするたびに、リクエストがサーバーに送信され、完全なレスポンスが毎回ダウンロードされます。
Last-ModifiedとIf-Modified-Since
Last-Modifiedヘッダーでは、最後に更新された日時をもとにリソースが同じかどうかを判断します。
また、リクエストにIf-None-Matchヘッダーが含まれる場合には、If-Modified-Sinceヘッダーが無視されます。つまり、Last-ModifiedヘッダーとETagヘッダーを併用した場合は、ETagヘッダーが優先されます。
EtagヘッダーとIf-None-Match
Etagヘッダーでは、「エンティティタグ」と呼ばれるリソースを特定するために割り当てられる固有の値(文字列)を用いて、リソースの内容が同じかどうかを判断します。
- Last-Modified オブジェクトが最後に変更された日時を示します。
- エンティティタグ (ETag) コンテンツの一意の識別子を提供します。
Cache-ControlもExpireもない場合はどうなるの
NginxもApacheも明示しなければ上記のヘッダーは自動ではつきません。自動でつかないなら全てのリクエストはオリジンへ飛ぶのかといういうとそんなこともなくブラウザの実装次第にはなりますが概ね以下のようなキャッシュがブラウザで行われます。
ヒューリスティックなアプローチを使用してキャッシュを利用する。レスポンスの生成時間とLast-modifiedの時間とコンテンツの時間を使って経験則から導き出した時間でキャッシュをする。ブラウザというかhttpクライアントごとに細かくは違うんだろう。
expirationTime = responseTime + freshnessLifetime - currentAge
正確な話はRFCに以下の記述があります。Ageヘッダーがない場合とかその辺はどうやってるのかは定義されていないのでこれもブラウザ依存でしょうか。
ブラウザにキャッシュはさせたいけど都度確認はしてきて欲しいとき
cache-control: public, max-age=0 ETag: "aaaaaa"
max-ageに0を指定する。クライアントは次回リクエスト時にEtagを使ってサーバに更新がないかどうかを問い合わせることでキャッシュが古くならないようにする仕組み。
from service workerって何
Etagとか使ったブラウザキャッシュはfrom disk cacheとかになるのかなとservice workerもキャッシュを持つらしいことを知った。上はtwitterにアクセスした際の静的ファイルのキャッシュ。
Service Worker とは、Webページとは別にバックグラウンド(別スレッド)で動作するJavascript環境のことでブラウザキャッシュとは違うところに保存されるらしい。
そしてどうやらClear Cacheとかのブラウザキャッシュをクリアするツールだけではこの領域はクリアされないらしい。
まとめ
HTTPレイヤでのキャッシュ、主にクライアントキャッシュについて調べてみた。サーバサイドのキャッシュと違ってクライアントの実装依存な部分があったりキャッシュの鮮度を意識したりとTTLだけの制御だけでは足りない世界を垣間見ることができました。
この辺の知識はRedis 6.0だかでリリースされた新機能のクライアントサイドキャッシュの理解にも役立ちそうでよかったです。