ブラウザがキャッシュの画像を使ってしまう

よくある事例だと思います。
画像を更新するためにアップロードしたら、その画像がすぐに反映されず、古い方の画像が残っている場合ですね。

これまで自身ではすぐに「キャッシュされてるな」と気づくので、F5キー押すなりすればいいだけとさして気にせず過ごしてきました。

記事にすることにしたきっかけ

最近ユーザーから、まさにこのケースに当てはまる問い合わせがありました。 「ハンコの画像をアップロードしても、古い方のハンコの画像が出てきます。直してください。」と。 当然のように、

それはキャッシュに残った画像が使われている状態だと思います。
F5キーを押すなどすれば、新しい画像が表示されるのでお試しください。

とお答えしました。これにて一件落着。。。。。ではなかった!

気付き

「エンジニアの方には常識なのかもしれないが、我々には今後も「バグ」に見えることが多いだろう。毎回問い合わせることになってしまうので、なんとかならないか?そちらも手間が減って楽では?」

なるほど・・・「たった1つ2つキーを押すだけ」と思っていた常識は、必ずしも常識ではないことに気付かせていただきました。

というわけで本題です

キャッシュ画像が使われてしまうことを回避する方法は、すぐにいくつか見つかりましたが、どれもイマイチでした。

たとえば.htaccess にキャッシュを禁止するように書く → 開発環境はapacheだからいいけど本番はnginexだった・・・とか。 まぁなんとかなるけど、WEBサーバーごとに設定するのも、他のキャッシュも止めてしまうのも何だし、とか。

採用した方法

htmlの<img>タグにパラメーターを付加することにしました。ただ、使い方に誤解があったので、その部分を共有しようと思います。

具体的な書き方:

<img src="hogehoge.jpg?[t=hhMMss]" alt="hankoimage">

[hhMMss]は、今回は「現在時刻」を入れる、という意図で、実はどんなものでも構いません。ただし、表示される都度、別の文字列が入るようにします。たとえば現在時刻が12:34:56だった場合、

<img src="hogehoge.jpg?t=123456" alt="hankoimage">

となるわけです。

Laravelを採用しているため、最終的にはこんな記述になりました。

<img src="{!! route('hogehoge.fugafuga_info.hanko_image', ['t'=>time()]) !!}" alt="hankoimage">

違和感

さきほど「使い方に誤解」と書きました。この実装をメンバーに依頼したところ、

アップロードする画像のファイル名にも[hhMMss]を追加して、”hogehoge123456.jpg”となるようしてくれていました。

「t=hhMMss を加えるのでファイル名もそろってないといけない」という考えでした。

実はひるやもり本人もきちんと理解できていなかったので、「これはなんか違和感がある。理解しよう。」ということになりました。

解説

結論からすると、まず、アップロードする画像ファイル名は、加工する必要はありません。上記の例では`”hogehoge.jpg”`のままで大丈夫です。

そして、['t'=>time()]の部分の見方ですが、

`”hogehoge.jpg”`というファイルをサーバーに要求する際に、ダミーの追加パラメーター ['t'=>time()]) を付与していて、しかもそのパラメーターは毎回変化するので、ブラウザが「別の画像が来るはずだ」という解釈をするため、キャッシュ画像が「使えない」と判断する。なので、キャッシュ機能を迂回させる(だます)ことができる。

ということのようです。

サーバーサイドには、プログラムに何も改変しません。追加パラメーター['t'=>time()]が付いてきますが、無視して処理していいので、何も変更しなくて良いのでした。

ただただ <img> タグの src= に、ダミーの乱数的なパラメーターを付与するだけで、キャッシュ機能が迂回できるのでした。

以上です。