HTTP キャッシュ

これまでの節で説明したサーバーサイドのキャッシュに加えて、ウェブアプリケーションは、同じページコンテントを生成し送信する時間を節約するために、クライアントサイドでもキャッシュを利用することができます。

クライアントサイドのキャッシュを使用するには、レンダリング結果をキャッシュできるように、コントローラアクションのフィルタとして [[yii\filters\HttpCache]] を設定します。 [[yii\filters\HttpCache]] は GETHEAD リクエストに対してのみ動作し、それらのリクエストに対する 3 種類のキャッシュ関連の HTTP ヘッダを扱うことができます:

  • [[yii\filters\HttpCache::lastModified|Last-Modified]]
  • [[yii\filters\HttpCache::etagSeed|Etag]]
  • [[yii\filters\HttpCache::cacheControlHeader|Cache-Control]]

Last-Modified ヘッダ

Last-Modified ヘッダは、クライアントがキャッシュした時からページが変更されたかどうかを示すために、タイムスタンプを使用しています。

Last-Modified ヘッダの送信を有効にするには [[yii\filters\HttpCache::lastModified]] プロパティを構成します。 このプロパティは、ページの更新時刻に関する UNIX タイムスタンプを返す PHP のコーラブルでなければなりません。 この PHP コーラブルのシグニチャは以下のとおりです。

/**
 * @param Action $action 現在扱っているアクションオブジェクト
 * @param array $params "params" プロパティの値
 * @return integer ページの更新時刻を表す UNIX タイムスタンプ
 */
function ($action, $params)

以下は Last-Modified ヘッダを使用する例です:

public function behaviors()
{
    return [
        [
            'class' => 'yii\filters\HttpCache',
            'only' => ['index'],
            'lastModified' => function ($action, $params) {
                $q = new \yii\db\Query();
                return $q->from('post')->max('updated_at');
            },
        ],
    ];
}

上記のコードは index アクションでのみ HTTP キャッシュを有効にすべきことを記述しています。 Last-Modified は、投稿の最終更新時刻に基づいて生成される必要があります。 ブラウザが初めて index ページにアクセスしたときは、ページはサーバ上で生成されブラウザに送信されます。 もしブラウザが再度同じページにアクセスし、その期間中に投稿に変更がない場合は、サーバはページを再生成せず、ブラウザはクライアントサイドにキャッシュしたものを使用します。 その結果、サーバサイドのレンダリング処理とページコンテントの送信は両方ともスキップされます。

ETag ヘッダ

"Entity Tag" (略して ETag) ヘッダはページコンテントを表すためのハッシュです。 ページが変更された場合ハッシュも同様に変更されます。 サーバサイドで生成されたハッシュとクライアントサイドで保持しているハッシュを比較することによって、ページが変更されたかどうか、そして再送信するべきかどうかを決定します。

ETag ヘッダの送信を有効にするには [[yii\filters\HttpCache::etagSeed]] プロパティを設定します。 プロパティは ETag のハッシュを生成するためのシードを返す PHP のコーラブルで、以下のようなシグネチャを持たなければなりません。

/**
 * @param Action $action 現在扱っているアクションオブジェクト
 * @param array $params "params" プロパティの値
 * @return string ETag のハッシュを生成するためのシードとして使用する文字列
 */
function ($action, $params)

以下は ETag ヘッダを使用している例です:

public function behaviors()
{
    return [
        [
            'class' => 'yii\filters\HttpCache',
            'only' => ['view'],
            'etagSeed' => function ($action, $params) {
                $post = $this->findModel(\Yii::$app->request->get('id'));
                return serialize([$post->title, $post->content]);
            },
        ],
    ];
}

上記のコードは view アクションでのみ HTTP キャッシュを有効にすべきことを記述しています。 Etag HTTP ヘッダは、リクエストされた投稿のタイトルとコンテントに基づいて生成されなければなりません。 ブラウザが初めて view ページにアクセスしたときは、ページがサーバ上で生成されブラウザに送信されます。 ブラウザが再度同じページにアクセスし、投稿のタイトルやコンテントに変更がない場合には、サーバはページを再生成せず、ブラウザはクライアントサイドにキャッシュしたものを使用します。 その結果、サーバサイドのレンダリング処理とページコンテント送信は両方ともスキップされます。

ETag は Last-Modified ヘッダよりも複雑 かつ/または より正確なキャッシング方式を可能にします。例えば、サイトが別のテーマに切り替わった場合には ETag を無効化する、といったことができます。

ETag はリクエスト毎に再評価する必要があるため、負荷の高い生成方法を使うと HttpCache の本来の目的を損なって不必要なオーバーヘッドが生じる場合があります。 ページのコンテントが変更されたときにキャッシュを無効化するための式は単純なものを指定するようにして下さい。

注意: RFC 7232 に準拠して EtagLast-Modified ヘッダの両方を設定した場合、HttpCache はその両方とも送信します。また、もし If-None-Match ヘッダと If-Modified-Since ヘッダの両方を送信した場合は前者のみが尊重されます。

Cache-Control ヘッダ

Cache-Control ヘッダはページのための一般的なキャッシュポリシーを指定します。 [[yii\filters\HttpCache::cacheControlHeader]] プロパティにヘッダの値を設定することで、それを送ることができます。デフォルトでは、以下のヘッダが送信されます:

Cache-Control: public, max-age=3600

セッションキャッシュリミッタ

ページでセッションを使用している場合、PHP はいくつかのキャッシュ関連の HTTP ヘッダ (PHP の INI 設定ファイル内で指定されている session.cache_limiter など) を自動的に送信します。 これらのヘッダが HttpCache が実現しようとしているキャッシュ機能を妨害したり無効にしたりすることがあります。 この問題を防止するために、HttpCache はこれらのヘッダの送信をデフォルトで自動的に無効化します。 この動作を変更したい場合は [[yii\filters\HttpCache::sessionCacheLimiter]] プロパティを設定します。 このプロパティには publicprivateprivate_no_expire、そして nocache などの文字列の値を使用することができます。 これらの値についての説明は session_cache_limiter() を参照してください。

SEO への影響

検索エンジンのボットはキャッシュヘッダを尊重する傾向があります。 クローラの中には、一定期間内に処理するドメインごとのページ数に制限を持っているものもあるため、キャッシュヘッダを導入して、処理の必要があるページ数を減らしてやると、サイトのインデックスの作成を促進できるかも知れません。