apacheのmod_cacheでお手軽キャッシュ機構
ユースケース
下記のような条件下にあるが、どうしてもキャッシュを使ってみたい場合に、mod_cacheは有効なのではというお話。
- Amazon CloudFrontやAkamaiなどのCDNが諸事情によって使えない場合
- お金をこれ以上かけられない
- ログを特定のフォーマットで出力する必要がある
- 既にアプリケーションの前段にApache(サーバ or コンテナ)がいる
キャッシュをさせる上で知っておく必要があること
上記の記事からいくつか掻い摘まんで読んでみます。
今回はブラウザのキャッシュではなく、プロキシのキャッシュ(共有キャッシュ)が主題になります。
キャッシュを制御する上で、必要になるのはCache-control
ヘッダです。リクエストおよびレスポンスでキャッシュ機能に関するディレクティブを指定するために使用します。
このヘッダは、レスポンスに付与された時にはプロキシでどのくらいキャッシュを保持するか(もしくはしないか)ということを設定できます。逆にリクエストに付与した場合には、キャッシュの鮮度を指定して、キャッシュから取得するのかオリジンから取得するのかが分かれます。
このヘッダが付与されていないリクエストはクエリパラメータが付いているリクエストをキャッシュの対象にしないことがRFCに明記されており、Apacheのmod_cacheの実装も同じです。
Cache-Control: no-store
- キャッシュストレージを一切利用しない = キャッシュに保存してはいけない
Cache-Control: no-cache
- いちどキャッシュに記録されたコンテンツは、現在でも有効か否かを本来のWebサーバに問い合わせて確認がとれない限り再利用してはならない
Cache-Control: private
とCache-Control: public
- publicの場合は共有キャッシュでキャッシュさせて良い
- privateの場合にはブラウザのプライベートキャッシュでしかキャッシュしない
Cache-Control: max-age=31536000
- リソースの鮮度を保証するもの
- 単位は秒数
responseTime + freshnessLifetime - currentAge
の時刻までキャッシュを保持することになる- もしこのヘッダーが与えられない場合には、
Expires
ヘッダーを見に行くことになる
Cache-control: must-revalidate
が付与されていると、キャッシュはリソースを使用する前に陳腐化の状態を検証しなくてはならない。ブラウザの再読み込みをしても、同じく検証が走る- キャッシュされた文書の有効期限に達すると
- HTTP/1.0との互換性を担保したい場合には、
pragma
ヘッダーを利用する(完全な大体にはならない)
キャッシュ機構を扱う上で重要となるレスポンスヘッダにVary
があります。キャッシュを提供する際に考慮すべきヘッダを指定します。指定したヘッダのvalueがキャッシュしたオブジェクトとリクエストで同一だった場合にはオリジンに新しくリクエストの発行を行わず、キャッシュを返却します。
例えばUser-Agentを指定した場合には、モバイルに対して誤ってデスクトップ版の画面を表示することがなくなります。
デバイスごとにキャッシュさせる
例えば、Amazon CloudFrontであれば、内部でデバイス判断ロジックを持っており、デバイスタイプに基づいてキャッシュを返却します
Apacheのmod_cacheはデフォルトだとシンプルなプロキシキャッシュを提供するので、判断ロジックなどはこちらで設定してあげる必要があります。
CloudFrontと同じような動作をmod_cacheにさせるためには以下のことを設定してあげる必要があります。
- レスポンスヘッダに
Cache-Control
を付与する - リクエストヘッダに独自定義のデバイスタイプのヘッダをセットする
- レスポンスヘッダにVaryヘッダを付与する(指定するヘッダは独自に定義したデバイスタイプ)
2つ目のリクエストヘッダの付与は、cacheハンドラが動く前に行う必要性があります。
mod_cacheはデフォルトだと、一番キャッシュ効率が良くなるように、設定ファイルへの記述順に関わらず一番最初に処理される作りになっています。
CacheQuickHandler off
と宣言することで、一番最初に処理されるのを避けることが出来ます。日本語の方のドキュメントには載っていないためハマりどころです。
最低限の記述としては下記のようになるかと思います
# 独自のデバイスタイプ用ヘッダを付与する RequestHeader set x-custom-device-type pc SetEnvIf User-Agent "<regexp>" device_tablet SetEnvIf User-Agent "<regexp>" device_mobile RequestHeader set x-custom-device-type mobile env=device_mobile RequestHeader set x-custom-device-type tablet env=device_tablet <IfModule mod_cache.c> <IfModule mod_cache_disk.c> CacheRoot /var/www/html/cache # リクエストヘッダのCache-Controlを無視 CacheIgnoreCacheControl On CacheDetailHeader on CacheEnable disk / CacheDirLevels 5 CacheDirLength 3 # mod_cacheが一番最初に処理を行わないようにする CacheQuickHandler off </IfModule> </IfModule> <Location "/"> Header append Vary x-custom-device-type Header append Cache-Control max-age=60 # 以下は省略