How to setup and use Rack::Cache with Rails
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
