ブラウザのキャッシュよるサイトパフォーマンスを向上させる方法

公開日:
更新日:
0ハイパフォーマンスWEB

ブラウザのキャッシュとは

ブラウザのキャッシュとは、Web ページを表示したときに取得したデータを一時的にローカルコンピュータに保存しておく仕組みのことです。次回以降、同じページにアクセスした場合、データを再度ダウンロードせず、ローカルコンピュータに保存されたデータを参照するためパフォーマンスが向上します。ただし、ローカルコンピュータに保存されたデータは最新のデータではない可能性があるため、古いコンテンツが表示される問題点があります。


このページでは、.htaccess によるブラウザのキャッシュを制御してサイトパフォーマンスを向上させる方法について説明します。.htaccess の設定や書き方などについては、.htaccessの書き方を参照して下さい。

キャッシュ有無の比較

ユーザが、初めて Web サイトに訪れたときはブラウザのキャッシュが空なので、HTTP リクエスト数に何も影響はありません。パフォーマンスに影響があるのは、キャッシュにデータがある状態でユーザがどれだけ頻繁にページを訪れるかで決まります。


例えば、月 1 回程度で 1 ページだけを見てすぐに離脱するページはキャッシュがある状態でページを参照する確率は低くなります。一方で、1 回の訪問でいくつものページを閲覧する Web サイトでは多くのページでキャッシュがある状態になります。


キャッシュの有無について Yahoo! が計測したところ、キャッシュがある状態で Web サイトに訪問した一意のユーザ数は、40 ~ 60 % を占めることが分かりました。同じ調査で、キャッシュがある状態でページを閲覧する回数は 75 ~ 85 % になることも分かりました。これらの結果から、多くのユーザがキャッシュを保持しており、パフォーマンスに大きく寄与していることが分かります。


Performance Research, Part 2: Browser Cache Usage - Exposed!

Expires ヘッダ

ブラウザはキャッシュを使用することで HTTP リクエストの数と、HTTP レスポンスのサイズを減らし、Web ページのローディング速度を高速化します。Web サーバは Expires ヘッダを使用して、指定の日時に到達するまでキャッシュを使用して良いことをクライアントに通知します。指定の日時を経過した場合は、キャッシュは無効であると判断され、再び最新のデータを取得します。以下は、.htaccess で Expires を指定する設定例です。


# Expires を指定する方法
ExpiresActive On
ExpiresByType image/png         "access 1 year"
ExpiresByType image/x-icon      "access 1 year"
ExpiresByType text/css          "access 1 month"
ExpiresByType text/x-javascript "access 1 month"
ExpiresDefault                  "access 1 days"
 Expires によるブラウザのキャッシュを有効にする設定例

ただし、Expires ヘッダの問題点として、サーバとクライアントで正確に時計が一致していることが求められます。また、有効期限を常にチェックしなければならないため、指定した日時が経過した場合は、サーバ側の設定を確認して新しい日時を指定する必要があります。この問題点を解決するために HTTP/1.1 では Cache-Control ヘッダが導入されました。

Cache-Control ヘッダ

Cache-Control は max-age ディレクティブを使ってコンポーネントをキャッシュする期間を秒単位で指定します。コンポーネントを要求した後、max-age で指定した秒数が経過していなければ、ブラウザはキャッシュされたものを使用し、余計な HTTP リクエストは送信しません。ただし、Cache-Control は HTTP/1.1 をサポートしていないレガシーブラウザには効果がありません。その場合、Cache-Control と Expires を両方記述することができます。両方記述されている場合は、Expires の設定を Cache-Control の設定で上書きします。


# Expires と Cache-Control を両方記述する設定例
ExpiresActive On

<FilesMatch "\.(html|css|js|png|ico)$">
  Header set Cache-Control "private, must-revalidate"
</FilesMatch>
<FilesMatch "\.(png|ico)$">
  ExpiresDefault           "access 1 year"
  Header set Cache-Control "max-age=31536000"
</FilesMatch>
<FilesMatch "\.(css|js)$">
  ExpiresDefault           "access 1 month"
  Header set Cache-Control "max-age=2592000"
</FilesMatch>
 Expires と Cache-Control を両方記述する設定例

FileETag

FileETag ディレクティブは、HTTP レスポンスヘッダに ETag(エンティティタグ)フィールドを作成するために指定します。ETag フィールドは、HTTP におけるキャッシュの有効性を確認するために使われます。例えば、ETag が同じ値である場合、コンテンツに変更がないと判断できるため、レスポンスをすべて返す必要がなくなります。そのため、キャッシュの効率化、および回線帯域の節約ができるようになります。


Apache 1.3.22 以前では、ETag の値は常に inode、ファイルサイズ、最終更新時刻から作成されていましたが、本ディレクティブによって以下の 5 つから選択できるようになりました。複数指定する場合は、半角スペースで区切って指定します。デフォルトの設定では FileETag INode MTime Size となっており、HTTP リクエスト・レスポンスの ETag フィールドでは - で連結されています。


  1. INode:ファイルの inode 番号を使います。
  2. MTime:ファイルの最終更新時刻を使います。
  3. Size:ファイルの Byte 数を使います。
  4. All:上記の 3 つをすべて使います。
  5. None:ETag フィールドをレスポンスに付加しません。

また、ETag は +- を上記のキーワードに付けることでディレクトリ毎の設定を変更することができます。例えば、上位ディレクトリで FileETag MTime Size を指定し、下位ディレクトリで FileETag -Size を指定すると、上位ディレクトリから継承した設定から Size が削除され、下位ディレクトリは FileETag MTime の設定になります。


# All は以下と同じです。
FileETag INode MTime Size
 FileETag の設定例

# ETag フィールドが付与されているレスポンス例
% curl -I https://murashun.jp/img/logo.png
HTTP/1.1 200 OK
Last-Modified: Wed, 15 Jul 2015 11:33:13 GMT
ETag: "acf9ef9-7ea-51ae851f07040"  # ETag が付与されている
Content-Length: 22761
 ETag フィールドが付与されているレスポンス例

ETag は、コンテンツが一致するかを決定する方法のひとつです。ブラウザがコンテンツ(例えば logo.png など)の ETag をキャッシュ後、改めて有効性を確認したい場合は、If-None-Match ヘッダを使ってサーバに ETag を送り返します。ETag が一致すると 304 ステータスコードが返され、レスポンスサイズが小さくなります。


# HTTP Request
GET https://murashun.jp/img/logo.png
Host: murashun.jp
If-Modified-Sine: Wed, 15 Jul 2015 11:33:13 GMT
If-None-Match: "acf9ef9-7ea-51ae851f07040"

# HTTP Response
HTTP/1.1 304 Not Modified
 コンテンツに変更がなかった場合のリクエスト/レスポンス

ただし、ETag の問題点として、負荷分散をするために Web サーバが複数台ある場合は、同じ内容のファイルであっても inode がサーバ毎で異なってしまうため、デフォルトの設定では 200 ステータスコードを返す可能性があります。その場合、パフォーマンスが低下し、サーバの負荷が高くなることになります。そのため、Web サーバが複数台ある場合は inode を使わないか、ETag 自体を使わない設定を検討する必要があります。

まとめ

ブラウザのキャッシュを使って高速化する方法はいくつかありますが、本サイトでは以下の設定を使っています。参考にされる場合は、適用する拡張子や、max-age の期間をサイトに合わせて最適化を行って下さい。


# murashun.jp のキャッシュ設定
<FilesMatch "\.(html|css|js|png|ico)$">
  FileETag None
  Header set Cache-Control "private, must-revalidate"
</FilesMatch>
<FilesMatch "\.(png|ico)$">
  Header set Cache-Control "max-age=15552000"
</FilesMatch>
<FilesMatch "\.(css|js)$">
  Header set Cache-Control "max-age=604800"
</FilesMatch>
 murashun.jp のキャッシュ設定