LoginSignup
154
131

More than 1 year has passed since last update.

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

Last updated at Posted at 2014-10-23

静的ファイル

Cache-controlヘッダを指定する

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

説明

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

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

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

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

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 (新鮮な; ウェブクライアントがキャッシュされたコンテンツをつかえる) かを真偽値で返す。

参考・引用

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

154
131
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
154
131