Webアプリでのキャッシュ制御方法

Webアプリでのキャッシュ制御する方法です。

Webアプリ内のキャッシュをうまく制御すると、コンテンツの応答時間短縮、回線有効活用、サーバリソースの節約といった性能対策になりますね。しかし、アプリケーションサーバで生成されるコンテンツ(JSP、PHPなど)は、ユーザーのセッション情報やDBアクセスなどを基に動的に生成されるものがほとんどです。そのため、ブラウザやProxyサーバにキャッシュされてしまうと、検索結果がいつまでも更新されなかったり、ほかのユーザーが検索した結果が参照できてしまったりするなどの不具合が発生する可能性があります。そのためキャッシュされないようシステム側でキャッシュコントロールが必要になってきます。

ここではWebアプリでのキャッシュ制御の方法と注意事項を紹介したいと思います。


キャッシュ対策設定

HTTP/1.1 には、キャッシュを制御する為の Cache-Control ヘッダが用意されています。

以下のような Cache-Control ヘッダをHTTPレスポンスに記述することによって、Webサーバからクライアントに送るコンテンツが、途中の経路上(プロキシ)でキャッシュに記録されたり再利用されたりしないように指示する事が出来ます。

Cache-Control内容クライアントプロキシ
publicクライアントまたはプロキシサーバにレスポンスキャッシュが可能とする。
privateクライアントにレスポンスキャッシュが可能とする。×
no-storeキャッシュは使用してはならない。××
no-cache1度、キャッシュに記録されたコンテンツが、現在でも有効か否かを本来のWebサーバに問い合わせて確認がとれない限り再利用してはならない。
must-revalidateキャッシュ可能であるが、Webサーバにリソースの再確認を要求する。

HTTP/1.0の考慮

Webサーバとクライアントの通信経路上に存在するプロキシサーバ等を全て把握することは困難です。場合によっては、古いサーバが存在し、HTTP/1.1 の Cache-Control ヘッダを解さないプロキシが存在する可能性もあります。

このような場合に、以下のヘッダをHTTPレスポンスに含めると、相手のプロキシが対応してくれる場合もあります。

Pragma: no-cache

ただし、この方法が常に有効であるという保証はありません。

キャッシュ制御サンプル

HTML4.xまでは Pragma, Cache-Control, Expires を meta タグを指定して制御します。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
<meta http-equiv="Pragma" content="no-cache"> 
<meta http-equiv="Cache-Control" content="no-cache"> 
<meta http-equiv="Expires" content="0">
 <title>HTML4.x no-cache</title> 
</head> 
<body> ... </body> 
</html> 

HTML5では CACHE MANIFEST ファイルで制御します。ドキュメントの html タグに manifest 属性を含めます。

<!DOCTYPE html> 
<html lang="ja" manifest="example.appcache"> 
<head> 
<meta charset="UTF-8">
 <title>HTML5 no-cache</title> 
</head> 
<body> ... </body> 
</html> 

manifest 属性は、絶対 URL と相対パスのいずれかを指定できますが、絶対 URL はウェブアプリの URL もしくはその配下である必要があります。

<html manifest="http://www.example.com/example.mf">・・・</html>

manifest 属性は、キャッシュ対象にするすべてのページに指定する必要があります。manifest 属性が含まれていないと、キャッシュマニフェストファイルに明示的に列挙されていない限り、ブラウザはそのページをキャッシュしません。キャッシュしたいファイルやページの制限はマニフェストで明示する方がいいかもしれませんね。キャッシュマニフェストファイルの拡張子に制限はありません。

マニフェストファイルの作り方はこんな感じです。

CACHE MANIFEST
# 2015-12-03:v1.0

CACHE:
style.css
logo.png
js/common.js

NETWORK:
*

FALLBACK:
/main.py /static.html
images/large/ images/offline.jpg
*.html /offline.html

CACHE MANIFEST はキーワードです。必ず1行目に記述します。

#で始まる行はコメントです。マニフェストファイルが更新されるとブラウザに通知され、キャッシュファイルが更新されます。キーワードのすぐ後にバージョンを入れておくと、CSSが更新された場合などに、ブラウザに通知することができます。

マニフェストには、CACHE、NETWORK、FALLBACK という 3 つのセクションを指定できます。セクションを指定する順序は自由です。

CACHE は、キャッシュさせるリソースの URI を列挙します。ワイルドカード("*")は利用できません。CACHE MANIFEST のすぐ後に記述します。

NETWORK は、サーバーへの接続を必要とするリソースの URI を列挙します。ユーザーがオフラインでも、これらのリソースに対するすべての要求はキャッシュをバイパスします。ワイルドカード("*")を利用できます。

FALLBACK は、リソースにアクセスできない場合のフォールバック ページを指定するオプションのセクションです。最初の URI はリソースで、2 つ目のリソースはフォールバック(代替)です。マニフェストファイルと同じ場所からの相対パスを指定します。ワイルドカード("*")も利用できます。

こちらのページが参考になりました。

アプリケーション キャッシュ初心者ガイド - HTML5 Rocks

キャッシュ制御が効かない場合

IEに限った話ですが、以下のページが参考になりました。

Pragma No-cache タグを使用してもページがキャッシュされることがある

<html>
<head>
<meta http-equiv="refresh" content="5">
<title> pragma no-cache </title>
</head>
<body>
これは、2 番目のヘッダー セクションを挿入して<br>
"pragama, no-cache" メタタグが正常に機能するようにした場合の例です。<br>
</body>
<head>
<meta http-equiv="pragma" content="no-cache"> 
</head>
</html>

なんと、まさかのヘッダを下に記述するという方法でした。

CSSやJSファイル等の読み込み時、ダミーのパラメータを設定

例えば JSP の場合、以下のようにダミーのパラメータに時間の文字列を設定することでキャッシュ制御することができます。

<link href="hoge.css?<%= System.currentTimeMillis() %>" ・・・
<script src="hoge.js?<%= System.currentTimeMillis() %>" ・・・

Javascript ではこんな感じで設定します。

showModalDialog( "calendar.html?"+ (new Date()).getTime(), ・・・

どちらも著しくパフォーマンスが落ちる場合、遅くなるページまたはファイルに対し、ダミーパラメータのシステム時間の文字列を固定値に変更し、ファイル変更の都度、その固定値を任意の値へ変更すれば対応できます。(手間はかかりますが・・・)


まとめ

Webアプリでのキャッシュ制御する方法を紹介しました。

キャッシュの恩恵は計り知れないものですが、逆に余計なお世話になることもしばしば。

昔は本番サーバーへリリースした後、ページが変わらなくて「なんで~?」って思うだけでしたけど、最近では「あ、これはキャッシュのせいね」みたいに割と早く解決できたりもします。

キャッシュさんは難しい人?なので、上手につきあっていきましょうね。

おつかれさまでした。

この記事がお役に立ちましたら シェア をお願いいたします。