キャッシュを保存させるヘッダーあれこれ

『Webを支える技術』(山本陽平著)みなさん読んでますか?webエンジニアは必須の読み物です。

その中でキャッシュについて詳しく触れていたので、備忘録がてらここにまとめます。

キャッシュとは

キャッシュとは、サーバーから取得したリソース(webページやらcssファイルやら)をローカルストレージ(ハードディスクなど)に蓄積し、再利用する手法のことです。

ローカルストレージにキャッシュしたデータそのものをキャッシュと呼ぶこともあります。

そのキャッシュが有効な間、クライアントが再度そのリソースにアクセスしようとした時、そのデータを取り出してきます。

どこかのサイトを開いたあと、またすぐに開いたら読み込み速度が全然違ったなんてことありませんか?それは、ブラウザでそのURIにアクセスしようとする前に、キャッシュデータベースを参照して、同じリソースがあるかどうか調べているからです。それがあれば、キャッシュの有効性を検証して、サーバに接続せずにそのページを表示します。

試しに、このページをリロードして見ると、最初に開いた時よりも早くリロード処理が終わると思います。(ブラウザによってまれにキャッシュが働かないこともあります)

キャッシュを有効・無効にするためのキャッシュ用ヘッダーをまとめます。

キャッシュ用ヘッダー

クライアントはサーバーから取得したリソースがキャッシュ可能かどうかを調べ、可能ならローカルストレージに保存します。

リソースがキャッシュ可能かどうか、その有効期限はいつまでなのかは次のヘッダーで指定できます。

  • Pragma
  • Expires
  • Cache-Controll
  • 条件付きGET
    • If-Modified-Since
    • If-None-Match
    • ETag

それぞれの使い方をまとめていきます。

Pragma

指定できる値:no-cache

Pragmaヘッダはこれだけです。

HTTP/1.1 200 OK
Content-Type: application/xhtml+xml; charset=utf-8
Pragma: no-cache

...

Pragma: no-cacheが指定されていたら、クライアントはローカルにキャッシュすることができないので、その都度サーバーからデータを持ってくる設定になります。

頻繁に内容が更新される様なwebページだと付けておくのが無難です。

Expires

指定できる値:日時

Pragmaがキャッシュを抑制するためのヘッダだったのに対し、Expiresはキャッシュの有効期限を示すヘッダです。

HTTP/1.1 200 OK
Content-Type: application/xhtml+xml; charset=utf-8
Expires: Thu, 11 May 2010 16:00:00 GMT

キャッシュ可能なデータ

このレスポンスでは、2010年の5月11日16時まではキャッシュが新鮮さを保つことをサーバが保証しています。

もし有効期限前であればキャッシュが参照され、有効期限を過ぎていればサーバのリソースを取りに行きます。

Expiresヘッダは1年以内の指定が推奨されています。

Cache-Controll

指定できる値:no-cache、max-age: 相対時間

Cache-Controllヘッダでは、キャッシュの抑制と有効期限のどちらも指定できます。有効期限を指定する場合には、リソースにアクセスした時間からそれくらい後まで有効かを指定します。

# Pragma: no-cache と同等
Cache-Controll: no-cache

# 2020-8-3 12:00:00 にアクセスしたら 2020-8-4 12:00:00 まで
Cache-Controll: max-age: 86400

Cache-Controllには他にも様々な識別子があります。

Cacheヘッダの使い分け

次の順番で評価していきます。

  1. キャッシュさせない場合は、PragmaとCache-Controllのno-cacheを同時に指定する。
  2. キャッシュの有効期限の日時が明確に決まっている場合、Expiresヘッダを使用する。
  3. キャッシュの有効期限を相対的に指定したい場合は、Cache-Controllのmax-ageで指定する。

条件付きGET

Exipires、Cache-Controllで有効期限が過ぎてしまった場合、条件付きGETの条件によってはキャッシュを再利用することができます。

If-Modified-Since

リクエストで指定した日時よりも後にリソースが更新されていなければ、サーバーはレスポンスでNot Modifiedを返し、キャッシュを再利用できます。

# リクエスト
GET /test HTTP/1.1
HOST: example.jp
If-Modified-Since: Thu, 11 May 2010 16:00:00 GMT
# レスポンス
HTTP/1.1 304 Not Modified
Content-Type: application/xhtml+xml; charset=url-8
Last-Modified: Thu, 11 May 2010 16:00:00 GMT   # Not Modifiedなら同じ日時

If-Modified-Sinceヘッダに対して、Last-Modifiedヘッダが返されます。

If-Modified-Sinceヘッダで指定した日時までに更新されていなければ、304が返されます。

もし304だった場合、レスポンスボディはなく、レスポンスヘッダのみなので、通信量の節約が期待出来ます。

If-None-Match / ETag

時計を持っていないサーバや、m秒単位で保存していくサーバなどには、If-Modified-Sinceヘッダが使えないといったことがあります。

その様な場合、If-None-MatchヘッダとETagヘッダを使います。

# リクエスト
GET /test HTTP/1.1
HOST: example.jp
If-None-Match: ab3322028
# レスポンス
HTTP/1.1 304 Not Modified
Content-Type: application/xhtml+xml; charset=url-8
ETag: ab3322028

ETagはリソースを作成、更新した際に作成される値で、この値で更新したか否かを判断します。

If-None-Matchヘッダに対して、ETagヘッダが返されます。

サーバーがETagを送出している場合はこちらの条件付きGETを用いると無難です。