質問: MacOSX SierraにHomebrewでapache2を入れ、png画像とhtmlを配置しています。 画像をキャッシュしたいと思い、以下のようにhttpd.confの設定をしました。 強制的にdiskキャッシュするためのモジュールのインポートと、diskキャッシュの設定と、ヘッダの設定です。 Last-modefiedとExpireがレスポンスヘッダに乗っていないとキャッシュされないと(どこかで)見たためこちらも追記しており、両方正常にレスポンスヘッダに乗っていることを確認しています。 ``` LoadModule cache_module modules/mod_cache.so LoadModule disk_cache_module modules/mod_disk_cache.so CacheRoot /tmp/cache CacheEnable disk / CacheIgnoreCacheControl On CacheIgnoreNoLastMod On CacheDefaultExpire 86400 CacheMaxExpire 172800 ExpiresActive on ExpiresDefault ExpiresByType image/png Header append Cache-Control "public, max-age=86400" Header set Expires "Mon, 26 Jul 2017 05:00:00 GMT" ``` その後、/tmp/cacheを作成しパーミッションを777にしてapacheをrestartし、chromeのdeveloperツールのネットワークを開き、htmlのaタグから画像にアクセスし、1回目でStatus 200、2回目で(from disk cache)と表示されていることを確認しているのですが/tmp/cacheには何もファイルが残っていません。また、chromeを再起動してから再び画像にアクセスするとStatus200となります。 Webサーバの設定でブラウザを再起動してもローカルのキャッシュを表示する方法はありますか? 1回目のアクセス時のレスポンスヘッダです。 ![1回目](https://i.stack.imgur.com/LyrSA.png) 2回目以降です。 ![2回目以降です](https://i.stack.imgur.com/jwXVk.png) ↓ ご指摘頂き、apacheの設定の仕方が間違っていたことがわかりました。 ``` Header append Cache-Control "public, max-age=86400" ``` を ``` Header set Cache-Control "public, max-age=86400" ``` とすることでChromeを再起動させてもapacheにアクセスログが表示されないことを確認しました。 ただ、safariではアクセスがされてしまい、固有のバグのようでした。 ローカルキャッシュの活用を考えていますが、やりたいこととしては必要のない画像取得の通信を出来るだけ減らしたい、更新のない同じ画像を何度もapacheからダウンロードさせるのは避けたいが更新があれば通信させるということです。 オフラインでも動くことは求めておらず、画像はバナーや商品画像など様々なものがあるためマニフェストファイルに画像を指定するApplicationCacheよりローカルストレージを使い、キャッシュを更新したいときはAPIから更新フラグを受け取り、更新する場合はclearCacheするという方法を用いたいと思います。 ↓ と思ったのですがAPIからキャッシュクリアするフラグを入れるのは悪手だということがわかりました。 どれだけの期間フラグをtrueにするかの問題とか、フラグがtrueになっている間はアプリを起動するたびにキャッシュ削除→画像再取得という状態になってしまうので、画像URLを変更しようと思います。
回答: なぜ mod_expires に加えて手動で `Header set` をしているのですか? mod_expiresはファイル形式に応じて、また「n日後」といったわかりやすい指定で Expires や max-age を設定できるモジュールです。例えば次の記述でPNG画像に対してキャッシュ期限が「アクセスから1ヶ月」となるヘッダが出力されるはずです。 ``` ExpiresActive on ExpiresByType image/png "access plus 1 months" ``` このように長いExpiresを指定した場合キャッシュの破棄をどうするかですが、よく使われる方法として、画像の更新時は画像のURLを変更する、というものがあります。単にファイル名を変えてもいいですし、クエリ文字列にタイムスタンプやハッシュ値を埋め込んでもよいです。 例: `foo.png?v=20170210` ⇒ `foo.png?v=20170308` こうすれば更新前とは違うURLなので、既存のキャッシュが使われることはありません。 ちなみに、Expiresが切れるまでの間は更新確認も行われないため、Last-Modified や ETag が使われることもありません。URLの更新が面倒なら、逆にExpiresを0にして`If-None-Match` や `If-Modified-Since` で妥協するのも手です。完全に通信をなくすことはできませんが。 次に localStorage ですが、これは非同期APIを持たないので、保存・取り出しのどちらもUIスレッドをブロックします。格納できるデータの量も5MB程度とさほど多くありません。IndexedDBならいくぶんマシですが...。 * ローカルストレージに簡単な解決策はない | Mozilla Developer Street (modest) * モダンブラウザのストレージ容量と調査方法まとめ - HTML5 Rocks キャッシュ絡みの問題でよく使われる「ブラウザのキャッシュ削除機能」「スーパーリロード」が使えない、ユーザー側のストレージでキャッシュ用でない領域を消費する、といった細かい話も。 またマニフェストファイルを使う ApplicationCache は既に廃止する方向で進んでいるのですが、その後継とされる ServiceWorker のキャッシュ機能なら、JavaScriptを使って柔軟に制御することができます。が、こちらはブラウザの対応状況が芳しくないです。 これらのことを踏まえて、それでもExpires(あるいは `If-**`)では不十分なのかを検討された方がいいように思います。