午後、思いがけずAlibaba DingTalkのフロントエンドの面接を受けることになり、大変嬉しく思いました。 面接の内容は、これまでのプロジェクトから、Angular、React、Expressなどの使用技術に及び、自身の理解がまだ不十分であると感じさせられました。 例えば、私がReactが好きで、大規模プロジェクトではデータ処理のためにReduxと組み合わせて使う必要があると話すと、Reduxが具体的にどのような問題を解決するのかと問われ、明確に答えることができませんでした。しかし、全体的に見て、面接の前半部分はかなり手応えを感じていました。 しかし、最後に面接官から「HTTPプロトコルでキャッシュをどのように制御しますか?」という比較的基礎的な質問が出ました。 私は戸惑いました。普段の業務では全く触れる機会がなかったからです。自分の感覚で、ファイル名の変更やHTTPのheaderに適切なパラメータを設定することで制御するとしか言えませんでしたが、具体的にどのように設定するのか、設定する内容は何なのか、全く答えることができませんでした。きっとここで不採用になるだろうと感じました。しかし、諺にもある通り「人は同じ場所で二度転ぶべからず」です。この機会にHTTPキャッシュの関連内容を記録しておきたいと思います。
概览
HTTPでキャッシュを制御する主な方法は以下の通りです。
ExpiresCache-ControlLast-Modified/If-Modified-SinceEtag/If-None-Match
それでは、一つずつ見ていきましょう。
Expires
有効期限です。cookiesのExpiresに少し似ています。headerに具体的な有効期限を設定でき、この有効期限内であれば、ブラウザはサーバーにこのファイルをリクエストせず、ローカルキャッシュから直接読み込みます。
上図は私のブログにあるjsファイルのHTTP headerです。赤枠1のExpiresが現在のDateから4時間後に設定されているのがわかります。つまり、通常このファイルはExpiresで指定された時間まではローカルキャッシュが直接使用され、サーバーから再取得されることはありません。 注意すべき点として、ExpiresはHTTP 1.0の機能であり、現在ほとんどのブラウザはデフォルトでHTTP 1.1を使用しているため、Expiresでキャッシュを制御することは推奨される方法ではありません。
Cache-control
Cache-controlはExpiresと基本的に同じ役割で、現在のリソースの有効期間を示し、ブラウザがローカルキャッシュを使用するか、サーバーからリソースを再取得するかを制御します。しかし、異なる点は、Cache-controlの方がより詳細な制御が可能であることです。headerにExpiresが同時に存在する場合、Cache-controlの優先順位が高くなります。
HTTPヘッダーCache-Controlの値は、public、private、no-cache、no-store、no-transform、must-revalidate、proxy-revalidate、max-ageのいずれかです。 各メッセージにおけるディレクティブの意味は以下の通りです。
Public 応答が任意のキャッシュによってキャッシュ可能であることを示します。
Private 個々のユーザーに対する応答メッセージ全体またはその一部が、共有キャッシュによって処理されてはならないことを示します。これにより、サーバーはユーザーの一部応答メッセージのみを記述でき、この応答メッセージは他のユーザーのリクエストには無効です。
no-cache リクエストまたは応答メッセージがキャッシュされないことを示します。
no-store 重要な情報が意図せず公開されるのを防ぐために使用されます。リクエストメッセージで送信されると、リクエストと応答メッセージの両方でキャッシュが使用されなくなります。
**max-age** **クライアントが指定された時間(秒単位)を超えない生存期間の応答を受け取ることができることを示します。**
min-fresh クライアントが現在の時間と指定された時間を加えた時間よりも短い応答時間を持つ応答を受け取ることができることを示します。
max-stale クライアントがタイムアウト期間を超過した応答メッセージを受け取ることができることを示します。max-staleメッセージの値を指定した場合、クライアントはタイムアウト期間の指定された値以内の応答メッセージを受け取ることができます。
図のように、赤枠2の上のCache-Controlの欄の値はpublic, max-age=14400です。これは有効期間が14400s、つまり4時間であることを意味し、上記のExpiresの有効期限と同じです。しかし、ExpiresのようにMon, 07 Mar 2016 17:14:33 GMTといった具体的な時間を指定する必要はなく、最大寿命の時間を指定するだけで済みます。これは比較的よく使われる方法でしょう。
Last-Modified/If-Modified-Since
**Last-Modified**はこのリソースの最終更新時間を示します。サーバーはリクエストに応答する際、ブラウザにこのリソースの最終更新時間を伝えます。**If-Modified-Since**HTTPリクエストを送信する際、ブラウザはキャッシュされたリソースの最終更新時間をサーバーに送信します。サーバーはこの時間とサーバー上の実際のファイルの最終更新時間を比較します。時間が一致すればHTTPステータスコード304を返し、ブラウザはそれを受け取るとキャッシュされたファイルを直接表示します。時間が一致しない場合はHTTPステータスコード200と新しいファイルの内容を返し、ブラウザはファイルを受け取るとローカルの古いファイルを破棄し、新しいファイルをキャッシュして表示します。Last-Modified/If-Modified-SinceはCache-controlと組み合わせて使用する必要があることに注意してください。ローカルのリソースが期限切れになった場合(つまり、max-ageで定義された時間を超えた場合)にのみ、If-Modified-Sinceを含むリクエストがサーバーに送信されます。
Etag/If-None-Match
**Etag/If-None-Match**も同様にCache-controlと組み合わせて使用する必要があります。**Etag**サーバーはブラウザのリクエストに応答する際、ブラウザに現在のリソースのサーバー上での一意な識別子を伝えます。識別子のルールはサーバーによって決定されます。If-None-Matchリソースが期限切れになった場合(つまり、max-ageで定義された時間を超えた場合)、リソースがEtagを宣言していることがわかると、再度サーバーにリクエストを送信する際にIf-None-Match(つまりローカルキャッシュされたリソースのEtag値)を含めます。サーバーはリクエストを受け取った後、If-None-Matchがあることを確認し、要求されたリソースのEtagと照合します。同じであればリソースに変更がないことを示し304を返し、そうでなければ200と新しいリソースを返します。
これら4つの方法の優先順位は、以下の図で説明できます。
画像引用元 Etagの優先順位がLast-Modifiedよりも高いことがわかります。
参考文章: 1.ブラウザキャッシュメカニズム 2.ブラウザキャッシュ関連の HTTP ヘッダー紹介
