HTTPキャッシュ

RFC2616 Hypertext Transfer Protocol (HTTP/1.1): CachingではHTTPキャッシュの目的を以下のように定めています。

HTTP/1.1 のキャッシングの目的はリクエストを送る必要を無くしたり、全レスポンスを送る必要を無くす事です。

BEAR.Sundayでは@Cacheableアノテーションによるサーバーサイドでのキャッシュが仕組みがありますが、これをRFC7234 HTTPキャッシュにも適用してネットワークキャッシュ(クライアントサイドキャッシュ)をサポートします。

REST標準のキャッシュ制約に従う事でRFC7234をサポートしたHTTPクライアントでは、クラアイントでのコーディングなしにクライアントサイドキャッシュやキャッシュサーバーの使用が可能になります。

Cache-Controlヘッダー

ディレクティブ

ディレクティブ 説明
public クライアント間の共有キャッシュの利用
private クライアントでキャッシュを共有しない
no-store キャッシュしません
no-cache キャッシュの利用にオリジンサーバーへの検証が必要。1
must-revalidate 期限の切れたキャッシュの利用に必ず検証が必要。
max-age キャッシュの有効期限
s-maxage 共有キャッシュの有効期限

publicとprivate

publicの場合、レスポンスにHTTP認証が関連付けられているとしても、レスポンスのステータスコードが通常キャッシュ可能になっていない場合でもキャッシュできます。通常はmax-ageなど明示的なキャッシュ情報によってレスポンスがキャッシュ可能であることが指定されているためpublicは必要ありません。

一方、privateレスポンスは、ブラウザのキャッシュには格納できますが、通常、対象ユーザーは1人のため、中間キャッシュに格納することは認められません。たとえば個人的なユーザー情報はそのユーザーのクライアントでのみキャッシュされCDNではキャッシュされません。

no-cacheとno-store

no-cacheはキャッシュ可能かどうか、つまりレスポンスに変更があったかどうかを確認する必要があることを示します。リクエストヘッダーの検証トークン(ETag)を検査し、リソースに変更がなければHTTPコード304を返答し、レスポンスボディを省略する事ができます。

no-storeは単純にレスポンスのすべてのキャッシュを禁止します。

###

最適なCache-Control ポリシーの定義2

@Cacheable

例)サーバーサイドで30秒キャシュ、クライアントでも30秒キャシュ。

サーバーサイドで指定してるのでクライアントサイドでも同じ秒数でキャッシュされます。この時に@HttpCacheアノテーションは必要ありません。

use BEAR\RepositoryModule\Annotation\Cacheable;

/**
 * @Cacheable(expirySecond=30)
 */
class CachedResource extends ResourceObject
{

例)指定した有効期限($body['expiry_at']の日付)までサーバー、クライアント供にキャッシュ

use BEAR\RepositoryModule\Annotation\Cacheable;

/**
 * @Cacheable(expiryAt="expiry_at")
 */
class CachedResource extends ResourceObject
{

@HttpCache

例)プライベートで30秒キャシュ

認証情報などクライアントで共用できないプラベート情報はisPrivateを指定します。

use BEAR\RepositoryModule\Annotation\HttpCache;

/**
 * @HttpCache(isPrivate=true, max-age=60)
 */
class CachedResource extends ResourceObject
{

例)更新されていないか都度確認します3

use BEAR\RepositoryModule\Annotation\NoHttpCache;

/**
 * @HttpCache(noCache=true)
 */
class CachedResource extends ResourceObject
{

例)上記と同じですが、更新されていないならプライベートキャッシュのみを利用します。

use BEAR\RepositoryModule\Annotation\NoHttpCache;

/**
 * @HttpCache(is_private=true, noCache=true)
 */
class CachedResource extends ResourceObject
{

@NoHttpCache

クライアントキャッシュしない時は@NoHttpCacheで指定します。

use BEAR\RepositoryModule\Annotation\NoHttpCache;

/**
 * @NoHttpCache
 */
class UncacheableResource extends ResourceObject
{

この時に@Cacheableでサーバーサイドでキャッシュすることは可能です。

レスポンスヘッダ

キャッシュされたレスポンスにはLast-ModifiedEtagAgeヘッダーが付加されます。 Last-Modifiedヘッダーはキャッシュが保存された時間、Ageヘッダーは保存されてからの経過時間が表示されます。

キャッシュがヒットしたかどうかはAgeヘッダー(キャッシュ生成後経過秒数)の存在で分かります。4

リクエストヘッダ

Varyヘッダーで指定したリクエストヘッダでレスポンスが変わることを指定できます。言語や、ユーザーエージェント等、認証ID別にキャッシュを持つことができます。

RFC7234対応クライアント

有効なキャッシュのために

  • 一貫したURIを使用する。5
  • クラアイントサイドでのキャッシュを優先して検討する
  • 適切な有効期限(max-age)を設定する。
  • ユーザー単位で格納されるリソースもprivateでキャッシュする。
  • 本当にキャッシュできないコンテンツのみno-storeを使用する。
  • HTTP APIクライアントにRFC7234対応クライアントを使用する。

  1. no-cacheという名前ですが、キャッシュは行います。 

  2. https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=ja より引用 

  3. ETagIf-not-modifiedリクエストヘッダーが使われます。 

  4. キャッシュテストに便利です。 

  5. 大文字と小文字が区別されます。