社内研修「HTTPから学ぶTCP/IP基礎」(4)

前回に引き続き、HTTPリクエストヘッダについてです。
「キャッシュ」という言葉をご存知でしょうか。もちろん現金のことではありません(笑)
よく使うデータや直前に使ったデータを一時的に保存しておき、次に使う時に本来の保存場所からではなく、一時的に保存している場所から取り出すことで、データの読み込みを高速化することです。
英語ではcacheと書きます。ちなみに現金はcashです。
HTTPでもWebブラウザが一度Webサーバから取得した情報を、ブラウザ内にキャッシュしておくことで、再度同じURLにリクエストするときにキャッシュを使用する、という仕組みがあります。
今回は、Webブラウザでのキャッシュの仕組みと合わせて、If-Modified-SinceIf-None-Matchについて説明をします。

キャッシュの仕組み

キャッシュは前述した通り、過去に使用したデータを一時的に保存しておき、再度使用するときに一時保存領域から取り出すことでデータの読み込みを高速化する仕組みです。
それならとりあえず1回アクセスしたWebサイトは全部キャッシュしておけば2回目以降はアクセスが減って高速になるし、便利なのでは?と思うかもしれません。ですが、一時保存領域のデータ容量の問題と、キャッシュを使用することで起こる、ある不都合なことがあるため、2回目以降キャッシュを使い続けることはできません。
ある不都合なことというのは、キャッシュした情報と最新の情報が異なっている可能性がある、ということです。
「それならキャッシュは使えないじゃないか!」「いや、キャッシュを使うならそんなの恐れちゃだめだ!」というような声が聞こえてきそうですが、
HTTPでは
・キャッシュが有効期限内で無条件でキャッシュを使用する
・有効期限は切れたが、最新であることを確認した上でキャッシュを使用する
という2段階の仕組みがあります。

Cache-Control

Cache-Controlは、HTTPレスポンスヘッダに含まれるヘッダフィールドです。いきなりHTTPレスポンスのメッセージヘッダの話が出てきますが、キャッシュが1回取得した情報を使用する手前、ご容赦ください。
Cache-Controlはキャッシュの有無および、キャッシュの有効期限を設定されます。
Cache-Control:”max-age=1800″
上記の場合は、1800秒間 (30分間)有効であるということになります。
Cache-Control: no-store
上記の場合は、ブラウザでキャッシュをしない、ということになります。
Cache-Control:no-cache
no-storeと似ていますが、no-cacheの場合はキャッシュをします。ただし、使うときにはサーバに問い合わせて最新であることを確認してから使う、ということになります。

また、Cache-Control以外にもExpiresヘッダを使って、
Expires:Fri, 2 Oct 2015 12:13:13 GMT
という書き方で、キャッシュの有効期限を表す方法があります。この方法はHTTP/1.0のバージョンからある古い方法ですが、Cache-Controlと併記する場合が多いです。Cache-Controlは有効な秒数ですが、Expiresは有効期限の日付時刻のため、Webサーバとブラウザ(PC)の時刻がずれていると正しく動作しない、という欠点があります。

ちなみに弊社WebサイトのトップページのHTTPレスポンスからは「何がなんでもキャッシュはさせない、使わせない」の意思が感じられます。
スクリーンショット 2015-10-02 21.55.46

>・キャッシュが有効期限内で無条件でキャッシュを使用する
こちらはCache-Controlの”max-age”設定により、キャッシュ有効期限が決まることがわかりました。
>・有効期限は切れたが、最新であることを確認した上でキャッシュを使用する
最新であることを確認する方法として、以下の2通りがあります。

LastModifiedとIf-Modified-Since

1つ目はLast-Modifiedを使った方法です。Last-Modifiedはレスポンスヘッダに含まれるフィールドです。
まず、Webサーバはキャッシュさせたいコンテンツの変更日時を、Last-Modifiedフィールドに入れてレスポンスを返します。
Webブラウザはコンテンツをキャッシュしておき、2回目のリクエスト時に、1回目のレスポンスに入っていたLast-Modifiedの値をIf-Modified-Sinceに設定してリクエストを送ります。
WebサーバはIf-Modified-Sinceの値を確認し、コンテンツに変更がなければ「変更がないのでキャッシュを使ってください」という情報のみ返し、コンテンツ本体は返してきません。コンテンツのデータサイズ分、通信量が少なくなり高速化されます。Webブラウザはキャッシュのコンテンツを使用します。

ETagとIf-None-Match

2つ目がEtagを使った方法です。ETagはレスポンスヘッダに含まれるフィールドです。
まず、Webサーバはキャッシュさせたいコンテンツのハッシュ値を作成し、ETagのフィールド値に設定します。
Webブラウザはコンテンツをキャッシュしておき、2回目のリクエスト時に、If-None-Matchに1回目のレスポンスに入っていたETagの値を設定してリクエストを送ります。
Web サーバはIf-None-Matchの値とコンテンツのハッシュ値を比較し、違いがなければ「変更がないのでキャッシュを使ってください」という情報のみ返 し、コンテンツ本体は返してきません。コンテンツのデータサイズ分、通信量が少なくなり高速化されます。Webブラウザはキャッシュのコンテンツを使用し ます。

実際に確認してみます。
TOPページはキャッシュを使わせないようにしていたので、弊社のロゴ(jpg)に対してリクエストを送ってみます。
スクリーンショット 2015-10-02 22.32.18
無事に画像ファイルが返って来ました。レスポンスヘッダにはLast-ModifiedとETagが入っています。
それではETagをIf-None-Matchに入れて、もう一度リクエストをします。
スクリーンショット 2015-10-02 22.40.59
「Not Modified」(変更なし)と返ってきており、レスポンス本体(画像ファイル)はありません。ブラウザはこのレスポンスから、キャッシュを使用するように判断します。
If-None-Matchを使用しましたが、If-Modified-Sinceを使っても同じ結果となります。
ちなみにRFC7232によると、If-None-MatchとIf-Modified-Sinceの両方を設定している場合は、If-None-Matchが優先されます。これはETagの方が更新をより正確に判断できると見なされるためです。

第4回は以上です。次回もHTTPヘッダが続きます。