試行錯誤して何とか形にした。
基本的なキャッシュ設定
ルートディレクトリの.htaccessファイルに、キャッシュを許可するファイル形式とその期間を記述する。ルートディレクトリ以下の全ページに適用される。
<ifModule mod_expires.c>
ExpiresActive On
# 各ファイルと保存期間
ExpiresByType image/png "access plus 1 weeks"
ExpiresByType image/jpeg "access plus 1 weeks"
ExpiresByType image/gif "access plus 1 weeks"
ExpiresByType image/webp "access plus 1 weeks"
ExpiresByType image/svg+xml "access plus 1 months"
ExpiresByType text/css "access plus 0 seconds"
ExpiresByType text/js "access plus 1 months"
ExpiresByType application/javascript "access plus 1 months"
ExpiresByType application/x-javascript "access plus 1 months"
ExpiresByType text/javascript "access plus 1 months"
# フォントファイルと保存期間
ExpiresByType application/x-font-ttf "access plus 1 year"
ExpiresByType application/x-font-woff "access plus 1 year"
ExpiresByType application/x-font-opentype "access plus 1 year"
ExpiresByType application/vnd.ms-fontobject "access plus 1 year"
</ifModule>
access plusで保存期間を指定。「0 seconds」にすると保存を拒否する。
許可しないファイル形式を指定する場合は、以下も加える。設置したディレクトリ以下にも適用される。上で許可したファイル形式を特定のディレクトリでのみ拒否したい場合などに用いる。
<FilesMatch "\.(js|css|jpe?g)$”>
Header set Expires "Tue, 1 Jan 2018 00:00:00 GMT"
Header set Cache-Control "no-cache,no-store,must-revalidate,max-age=0"
Header append Pragma "no-cache"
</FilesMatch>
キャッシュを拒否したいファイル形式を記入する。この例の場合だと「.js」「.css」「.jpg」「.jpeg」ファイルのキャッシュを拒否することになる。
ちなみに「no-cache」はキャッシュの再利用拒否、「no-store」はキャッシュに記録することを拒否するよう、それぞれ指示する。
ページ単体でキャッシュを拒否する場合はよく見るmetaタグで指定する。
<meta http-equiv="Cache-Control" content="no-cache">
ただし、後述する「bfcache」を備えているブラウザではno-storeなどは機能しない。
JavaScriptのキャッシュ対策
JavaScriptに対するキャッシュ対策はそこそこ手間がかかる。
特に「bfcache」搭載ブラウザのキャッシュが強固で、前述の設定だけではJavaScriptのキャッシュを勝手に再利用するので、下記の対策手段をとる。
対策1
外部ファイルでJavaScriptを読み込んでいる場合、ファイルの参照タグに手を加える。
<script src="setting.js?<?php echo date('YmdHis'); ?>" type="text/javascript"></script>
ファイル名(今回はsetting.js)の後ろに ? を加えてその後に適当な文字列を記述すると、別ファイルと認識して、キャッシュから読み込もうとしない。
とはいえ、更新の度に毎回文字列を弄って異なる文字列にするのは面倒で、それが複数のページにわたるとなると更新時の作業量が尋常ではない。そのため、 ? の後ろに来る文字列を毎回自動的にランダム生成させる。
今回の場合、phpのdateコマンドを追加した。現在の年(Y)・月(m)・日(d)・時(H)・分(i)・秒(s)を付与させている。cssなどにも適用可能。
ただ、このコマンドだと、アクセスの度に常に新しいファイルとして永遠に認識し続けてキャッシュの削除を常に繰り返すため、必要な場合にのみ新しいファイルとして認識しキャッシュの削除を行うような仕組みにすべき。
その必要な場合というのは、主にファイルを最後に更新した場合であるため、phpで「ファイルの最終更新日」を参照するように指示する。
<script src="common.js?<?php echo date('ymdHis',filemtime( 'common.js')); ?>" type="text/javascript"></script>
filemtime関数はファイルの最終更新日を取得してくれるので、これで対象のファイルを参照させ最終更新日を取得し、 ? の後にその日付時間が付与され、ファイルを更新した場合にのみ(ファイル名の後ろの日付が変わるので)キャッシュの削除が行われる。
ちなみに参照するファイルは相対パスで記載する必要があり、絶対パスだと機能しない模様。
また、書式としてphp部分は「”」ではなく「’」を用いるのが正しい。「”」でも動作はするが最適解ではない。
対策2
ChromeやiOS版Safariなどには「bfcache」という機能が備わっており、「ページ遷移前の状態をメモリキャッシュとして保存し、そのページに戻ってきた時にそのキャッシュを表示する」という動作が起こる。
JavaScriptが実行されている間にページを移動して戻ってくると、実行されている最中の状態でページが開かれる。「ハンバーガーメニューを展開中に移動して戻ったら展開したままだった」というのがよくある例。
そのため、ChromeやiOS版Safariなどへの対策をする必要があるので、ブラウザバック時にキャッシュを削除してもらうため、下記を記述。
window.onpageshow = function(event) {
if (event.persisted) {
window.location.reload(true)
}
};
イベント部分はjQueryでは不可なのでちゃんとJavaScriptで書く。
onpageshowで、ロードされた際にイベントが発動するように書く。
event.persistedで、bfcacheの発動の有無を確認。bfcacheの発動している場合の処理をifのtrueにあてる。今回はbfcacheが発動している場合に自動でリロードする処理。
ちなみにフォームが設置してあるページでこれを読み込むと、
- autocomplete=”off” を設定していない
- 送信ボタンが type=submit になっている
- ページの有効期限を設定している
- HTTPヘッダーでキャッシュ許可
などの点を踏まえていても、「記述→送信→確認→戻る」の動作を行った際に記述内容が消えてしまうという事態に陥るので注意。