cssやjsのキャッシュをタイミング良く消したい

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
これがランヴァイル何とかかんとかゴゴゴホ駅です

おまとめ三行

キャッシュカードを使って銀行からお金を下ろすよりはるかに簡単
(○`ε´○) って顔をしてやれ
Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch

ウェブサービスの改修を繰り返していると、ときどきユーザーさんから「サイトが動かねえ」「見た目が崩れてて見辛い」的な問い合わせが来ることがあります。でもこっちで動きを確認してみると、特に問題はない。一通りのブラウザでチェックしてみてもやっぱり問題はない。

そういうときはたいてい、キャッシュの問題だったりします。

つまりcssやjsの中身を改修して更新したは良いものの、ユーザーさんの環境にはキャッシュが残ってるせいで更新前の状態のcssやらjsが読み込まれて、結果デザインが崩れたり挙動がおかしくなったりするわけですね。

この場合はキャッシュをクリアすれば問題は解決するわけですが、毎回ユーザーさんにそれを言うのもめんどい。しかも「キャッシュを消してください」って言ってユーザーさんがすんなり理解してくれれば良いですが、「キャッシュって何ですか?」って人だって中にはいるでしょう。それを考えたら、適当なタイミングでキャッシュがクリアされるよう、こちら側で制御できるのが良いですよね。

ってことで、やってみましょう。そんな難しくないっす。キャッシュカードを使って銀行からお金を下ろすよりはるかに簡単な作業です。



キャッシュさせない

まず、一切キャッシュをさせない方法。つまりページを読み込むたびに毎回サーバーからファイルを参照するってことですね。これなら確実に最新の状態のファイルを読み込むことができます。

やり方はいくつかあると思うんですが、簡単なのはファイルの末尾にタイムスタンプのパラメータを追加することでしょう。

<?php $timestamp = time() ?>
<link rel="stylesheet" type="text/css" href="style.css?<?php echo $timestamp ?>">
<script type="text/javascript" src="util.js?<?php echo $timestamp ?>"></script>

//出力結果の例
<link rel="stylesheet" type="text/css" href="style.css?1435935600">
<script type="text/javascript" src="util.js?1435935600"></script>

こんなんですね。「time()」を使って現在時刻のタイムスタンプを取得し、それをパラメータにくっつけてます。上記の「1435935600」は2015年7月4日の0時ちょうどのタイムスタンプです。

ファイル名が変わらなくても、パラメータの値が変わればブラウザはサーバーからファイルを読み込んでくれるので、これならキャッシュは無効化されます。まあ「$timestamp」の値は秒単位で変わるから、一秒以内にページを読み込み直せばキャッシュが効くけど……そんな重箱の隅をつつくようなことを言う人には (○`ε´○) って顔をしてやれ。

パラメータの書き方はどんなんでも良いです。「style.css?1435935600」でも「style.css?time=1435935600」でも効果は同じです。ファイル名の後ろに「?」をつけるのを忘れなければ「style.css?Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch=1435935600」とかでも大丈夫……なのかな?

ちなみに「Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch」ってのは、世界で一番長い駅の名前です。イギリスにあります。読み方は「ランヴァイル・プルグウィンギル・ゴゲリフウィルンドロブル・ランティシリオゴゴゴホ」



特定のタイミングでキャッシュさせない

上記のやり方だと常にキャッシュが効かないですが、基本的にはcssやjsのファイルが更新されたタイミングでキャッシュが効かないようになれば良いわけで、何も毎回サーバーから読み込む必要はない。

そういう場合には「filemtime()」ってのを使います。

<link rel="stylesheet" type="text/css" href="style.css?<?php echo filemtime('style.css') ?>">
<script type="text/javascript" src="util.js?<?php echo filemtime('util.js') ?>"></script>

「filemtime()」は、ファイルの更新時間を取得する関数です。これなら、ファイルが更新されるまでは値が変わらないのでキャッシュが効く。更新された後にページを読み込むタイミングで、サーバーから読み込んでくれる。

「filemtime($file)」の$fileは、URLではなくファイルのパスです。「http://norm-nois.com/style.css」みたいなことではなくて、「/var/www/html/style.css」的なことです。



CakePHPの機能を使ってキャッシュさせない

機能ってほど大層なもんでもないんですが、CakePHPを使っている場合、core.phpの中にこんなのがあります。

Configure::write('Asset.timestamp', true);

ない場合はバージョンが古いんだと思うから、バージョンを上げてオッケーな状況ならこの機会に上げちゃう?

デフォルトではコメントアウトされてるかもしれないけど、これを有効にすると、上記のfilemtime()を使う場合と同様のことを、Cakeさんが勝手にやってくれます。と言っても、Htmlヘルパーを使ってcssやjsを読み込めばの話ですけどね。自分でscriptタグとかをベタ書きした場合は、もちろんやってくれません。

引数には「true」と「force」の二つが使えるようで、trueだとデバッグを出力する設定にしているときに、filemtime()的なことをやってくれます。forceだと常にやってくれるので、本番環境でもキャッシュを効かせないようにしたければ、forceにすればオッケーってことですね。

//core.php
Configure::write('Asset.timestamp', 'force');

//view
<?php echo $this->Html->css('style') ?>
<?php echo $this->Html->js('util') ?>

こんな感じですかね。






正直、CakePHPのAsset.timestampは最近まで気づきませんでした。だから自分でHtmlヘルパーを改造してfilemtime()をくっつけるようにしてたんですけど、無駄な努力だったさ (○`ε´○)

常にキャッシュをさせない方が良いのか、ファイルの更新時間に合わせるのが良いのか、その辺は運用状況にもよるかもしれないんで、使える方を採用するってことで、キャッシュ問題にお困りの方は、お試しあれ〜。

まだコメントはいただけてないみたい……
もしかしたら何か関連しているかも?