How to setup and use Rack::Cache with Rails

Ruby posted over 4 years ago by christian

Add Rack::Cache to your Gemfile:

   1  gem 'rack-cache', :require => 'rack/cache'

Configure Rails to use Rack::Cache by adding this to config/environment.rb:

   1  config.middleware.use Rack::Cache,
   2    :verbose => true,
   3    :metastore   => 'file:/var/cache/rack/meta',
   4    :entitystore => 'file:/var/cache/rack/body'

This will cache content in /var/cache/rack/meta. Check the documentation for instructions on how to use memcached.

Verify configuration

Verify the configuration:

   1  rake middleware

You should see something like this:

   1    2  use Rack::Cache, {:metastore=>"file:/var/cache/rack/meta", :entitystore=>"file:/var/cache/rack/body", :verbose=>true}
   3 

Tell Rack::Cache to cache your content

Tell Rack to cache data by calling expires_in from your controller’s action or from a Rails filter:

   1  expires_in 5.minutes, :public => true

Rails sets cache-control to private by default, Rack::Cache needs public content.

Don’t cache private data

Note that you must be careful not to cache private data when a user is signed in. In this case you should set the Cache-Control header to private or completely avoid using expires_in:

   1  expires_in 5.minutes, :public => true if signed_out?

Verify caching

With verbose set to true, you’ll see this in the logs:

   1  [cache] trace: cache miss
   2  [cache] trace: fetching response from backend
   3  [cache] trace: store backend response in cache (ttl: 300s)
   4  [cache] trace: storing response in cache
   5  [cache] trace: delivering response ...
   6  [cache] trace: cache hit (ttl: 276s)
   7  [cache] trace: delivering response ...

Caching more than one type of content

Make sure you cache e.g. HTML and JSON separately. You could use the Vary HTTP header to achieve this:

   1  response.headers['Vary'] = 'Accept'

The problem is that almost every browser version has a unique Accept header.

Instead you could try using the cache_key configuration option to generate a cache key per content type:

   1  :cache_key  =>  lambda { |request|
   2    Rack::Cache::Key.new(request).generate + ":jebus"
   3  }

You could also add the format request parameter to the URL, e.g. /json-and-html?format=html

Advanced configuration

In production you probably want to disallow reloading of content. This can be done with the following configuration options:

   1  config.middleware.use Rack::Cache,
   2    :metastore   => 'file:/var/cache/rack/meta',
   3    :entitystore => 'file:/var/cache/rack/body',
   4    :allow_reload     => false,
   5    :allow_revalidate => false,

Read the source (options.rb) for more advanced configuration options (ignore_headers, private_headers, etc).

References

HTTP RFC – Cache-Control
Rack::Cache options documentation
Rails expires_in documentation
HTTP Vary Header

Tagged rack, rack::cache, cache, rails