クラウドのホスティングサービスは、一定リソースの時間極課金+通信トラフィックの従量課金が一般的です。
CPUやメモリなどのリソースは、1%しか使わなくても100%使っても時間内の料金は同じです。
一方で通信料は使った分だけGB単位などで段階的に課金される仕組みです。
この料金体系では、なるべくリソースを使い切って、且つ通信料を抑えることが最も費用対効果のある利用方法となります。
サーバーからクライアントへのレスポンス、特にブラウザーのロードとレンダリングを高速化させるために、Yahoo!のYSlowやGoogleのPage Speedを使ってチューニングを行うのと同じようなアプローチで、なるべくCPUに仕事をさせて、トラフィックを減らしてみたいと思います。
キャッシュ機能を最大限利用する
Expires
Apacheのmod_expiresを有効にすることで、レスポンスヘッダーにExpiresフィールドが出力され、最初のアクセス以降設定した期間が経過するまでブラウザーはローカルのキャッシュを利用し続けます。
この設定は強力で、ローカルのキャッシュがなくなるか、強制リロードをしない限り、サーバーへアクセスすらしません。
(逆に、更新をかける余地のあるファイルには設定しない方がいい項目です。)
LoadModule expires_module modules/mod_expires.so ExpiresActive On ExpiresByType application/javascript "access plus 1 days" ExpiresByType application/x-javascript "access plus 1 days" ExpiresByType text/javascript "access plus 1 days" ExpiresByType text/css "access plus 1 days" ExpiresByType image/jpeg "access plus 3 days" ExpiresByType image/png "access plus 3 days" ExpiresByType image/gif "access plus 3 days" ExpiresByType image/x-icon "access plus 3 days"
有効期間の設定には以下の単位も利用できます。
- years
- months
- weeks
- days
- hours
- minutes
- seconds
Last-Modified、Etag
次に、キャッシュが切れていよいよブラウザーがアクセスをしてきた時のための設定を施しておきます。
特段設定をしないとApacheはLast-ModifiedとEtagフィールドをセットしたヘッダーを送信します。
この値を受け取っているブラウザーは、Last-Modifiedの値をIf-Modified-Sinceに、Etagの値をIf-None-Matchに含めてリクエストを送信します。
受け取ったサーバーは値を比較して変更が無ければコンテンツの中身は返さず、304 Not Modifiedを応答します。
コンテンツを返さない分トラフィックは抑えられるわけですが、この二つのフィールドはコンテンツを返すか、返さないかを判断するという意味では同じで、解釈の仕様によって動きが変わってきます。
Apacheにおいては、If-Modified-SinceよりもIf-None-Matchが優先されます。
If-Modified-SinceはIf-None-Matchがない場合にだけ利用され、If-Modified-Sinceの値が同じでも、If-None-Matchの値が変更されていた場合には、コンテンツを返します。
これだけ優先されるIf-None-Matchなので、ETagの値を間違ってセットするとサーバーが毎回コンテンツを返してしまいます。
この辺りでよく問題にされるのは、負荷分散環境下で複数のサーバーが同一コンテンツを返しているような場合です。
クラウドのホスティングサービスを選択するようなケースでは、スケールアウトする仕組みが期待されることが多いので、あらかじめ設定しておくべき項目です。
Apacheがデフォルトで出力するETagが「ファイルの inode番号、ファイルサイズ、更新時刻」を元に生成するので、inode番号がサーバーによって変わってしまう環境では、せっかくIf-None-Matchを送っていても受け取るサーバーごとで比較するETagが違い、毎回無駄にコンテンツを返してしまいます。
以下のサイトに解説や実例があります。
- 堀愚霊瑠の指摘で気付いた、はてなスターの静的ファイルとか想像以上にアレな件 – にぽたん研究所
- Webサイトの高速化 ルール13 ETagを正しく設定する! (Yahoo! developer netoworkより翻訳) | 株式会社インターオフィス | インターオフィス
- DSAS開発者の部屋:負荷分散環境でブラウザキャッシュが効かないときは – ETagの解説 –
これを回避するために、ETag自体を出力しない方法と、ETagの生成にinode番号を利用しないようにする方法と2通りあります。
ETag自体の出力をしない
FileETag None
ETagの生成にinode番号を利用しない
FileETag MTime Size
どちらにせよ、これらの設定では「最終更新日」が重要な要素になってきます。
ETagを出力しない場合はLast-Modified(最終更新日)での判定が有効になり、inode番号を利用しない場合はファイルサイズと最終更新日がベースとなったEtagでの判定になるからです。
最終更新日が全てのサーバーで同じになるように、ファイルをコピーする時、同じタイムスタンプになるように注意してコピーしなければなりません。rsyncであれば-tオプションを付けてコピーします。
ETagを出力しない場合は、内容に変更があったファイルでもタイムスタンプが同じであれば同一ファイルと見なされるリスクがあります。
inode番号を利用しないでETagを出力した場合は、その分リクエスト・レスポンスヘッダーともに微増し、トラフィックに若干影響があります。
トラフィックを抑えることが最優先であれば出力しない、ファイルの管理を厳密に行いたいのであれば出力する、ETagの設定をどちらにするかは状況によって変わってくると思います。
今回の趣旨では、ETagは出力しない方がいいということになります。
コンテンツは基本、圧縮
mod_deflateモジュールを有効にすると、設定に従ってレスポンスをgzipで圧縮して送信してくれます。
Deflateによる圧縮はCPUに働かせ、トラフィックを減らす、クラウド環境にもってこいの設定です。
LoadModule deflate_module modules/mod_deflate.so AddOutputFilterByType DEFLATE text/html AddOutputFilterByType DEFLATE text/css AddOutputFilterByType DEFLATE text/xml AddOutputFilterByType DEFLATE application/javascript
Webで利用される画像フォーマットはそもそも圧縮されている場合が多いので、圧縮効率の高いテキストファイルを対象にします。(WordPressの場合、WP Super Cacheを使うことで、記事自体もgzip圧縮して送信できるようになります。)
使える外部リソースを使う
これは番外編です。
RSS、各種APIなどのWebサービスからOpenIDやOAuthなどのリモート認証、Webは外部のリソースと協調する方向へと進んでいますから、利用できるリソースをどんどん活用するのもトラフィック節約になります。
flickrやYouTubeなどをホスティング的に利用するのはもちろん、JavaScriptのライブラリはGoogleのAJAX Libraries API、アバターアイコンはGravatarなど、目的に合わせて使える外部リソースを使っていきましょう。
もちろん外部リソースに依存することはリスクでもあるので、いい面ばかりでもありません。1ページに複数ドメインへのリソースがあると、名前解決に時間がかかりページのロードが遅くなるなどの問題もあります。
ご利用は計画的に。