Rails でブラウザのキャッシュを利用する

  • 91
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

静的ファイル

Cache-controlヘッダを指定する

Expiresと同じく、キャッシュの有効期限を指定できるレスポンスヘッダ。HTTP1.1で定義されたもので、有効期限だけでなくキャッシュに関する詳細な設定を行える。大多数のブラウザが HTTP1.1に対応しているので、Expiresではなく、Cache-Controlヘッダを利用するのがよい。

説明

  • max-age : キャッシュの有効期限を設定する。(相対時間で指定し、単位は秒。)

    例えばブラウザのキャッシュを1年間有効にしたい場合は、次のように指定する。
    有効期限の間はキャッシュが利用され、サーバに問い合わせが発生しないようにできる。

Cache-Control: max-age=31557600
  • private : そのコンテンツがそのユーザのみに対して提供されたものであり、キャッシュを他のユーザと共有しない。

  • must-relalidate : キャッシュに記録されているコンテンツが現在も有効かどうかを、必ずサーバに問い合わる設定。

Rails では

以下がデフォルトでレスポンスに付与している。

Cache-Control: max-age=0, private, must-revalidate

Cache-Control ヘッダを Rails から指定したい場合は、コントローラで次のように記述する。

def index
  expires_in 1.hour
  # => Cache-Control: max-age=3600, private
end

expires_in はオプションとして、public と must_revalidate を T/F で指定する。

ブラウザにキャッシュさせたくない場合

def index
  expires_now
end
# => Cache-Control: no-cache

Nginx で同様のの設定をするには

nginx.conf
location ~ ^/assets/ {
  add_header Cache_Control "max-age=31557600, public";
}

これで、有効期限の間はキャッシュが利用され、サーバに問い合わせが発生しないが、活用できるのは静的ファイルに限られる。

動的ファイル

ETag、Last-Modified ヘッダを指定する

説明

  • ETag(If-None-Match)
    そのコンテンツを示すユニークな値を返すレスポンスヘッダで、コンテンツの更新の有無を確認するために用いられる。同じURLへリクエストを行った場合、コンテンツが更新されていなければ ETag ヘッダの値は毎回同じ、コンテンツが更新されていれば ETag ヘッダを変化させるという決まり。Etag ヘッダを設定すると次回のリクエストのヘッダに、If-None-Match というリクエストヘッダを付し、この値を比較し、ページが更新されていなければ 304 - Not Modified のステータスコードのみを返す。

  • Last-Modified(If-Modified-Since)
    Etag と同様に、条件付き GET リクエストのためのレスポンスヘッダ。名前の通りそのURLが最後に更新された時刻を出力する。Last-Modified ヘッダを設定すると If-Modified-Since というリクエストヘッダを用いる。

Rails では

def index
  @article = Article.last
end
# => 200 OK

def index
  @article = Article.last
  fresh_when @article
end

# => 304 Not Modified
# => Etag : "<MD5>"
# => Last-Modified: <日付>

fresh_when

Etag と Last-Modified をレスポンスヘッダに付与する。

fresh_when は Rails4.2 から template の更新が考慮されるように修正されたみたいです。

rails commit log流し読み(2014/08/17) - なるようになるブログ : http://y-yagi.hatenablog.com/entry/2014/08/18/063227

コントローラレベルでの ETag

class ArticleController < ApplicationController
  etag { current_user.try(:id) }

  def index
    @article = Article.find(params[:id])
    fresh_when(@article)
  end
end

etag はブロックの評価結果を ETag の値を算出する元データに追加します。ログインユーザごとに ETag の値を変更したい場合に有効。

コード

今回使ったメソッドは、
ActionController::ConditionalGet にある。
https://github.com/rails/rails/blob/b40bd16b7b0810b9d5621339f243c4a9569ceb96/actionpack/lib/action_controller/metal/conditional_get.rb

  • expires_in, expires_now
  • fresh_when
  • stale? (上で紹介していない)

適切なEtagとLast-Modified-Sinceヘッダを設定し、現在のリクエストがstale (腐りかけた; 完全な処理をする必要がある) か、それともfresh (新鮮な; ウェブクライアントがキャッシュされたコンテンツをつかえる) かを真偽値で返す。

参考・引用

以下の書籍、記事から多く引用・参考にさせていただきました。
スライドや図、サンプルコードなどもあってとてもわかりやすいです。