Page caching with Rails 4

20 Oct 2013 By: Greg Molnar

The built-in page caching has been extracted to a separate gem in Rails 4 and here is a guide how to use it. First we need to add the gem to our Gemfile:

gem 'actionpack-page_caching'

Than in the application controller we need to specify the folder to store our cache files:

class ApplicationController < ActionController::Base
  include ActionController::Caching::Pages
  self.page_cache_directory = "#{Rails.root.to_s}/public/page_cache"
end

Let's say we have an article controller and we want to cache the index and the show action:

class ArticleController < ApplicationController
  caches_page :index, :show
  # Rest of the file omitted.
end

Now if we have the config.action_controller.perform_caching set to true Rails will generated the HTML output of the pages in the cache folder. One more thing we need to do is to tell the webserver to use the cached version if there is one so the request won't even hit our app. With nginx we can achieve this with a configuration like this:

upstream puma_server_domain_tld {
  server unix:/path/to/the/puma/socket;
}
server {
  listen 80;
  server_name domain.tld;
  root /path/to/the/app;
  location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    # try the $uri, than the uri inside the cache folder, than the puma socket
    try_files $uri /page_cache/$uri /page_cache/$uri.html @puma;
  }
  location @puma{
    proxy_pass http://puma_server_domain_tld;
    break;
  }
  # set the expire date to max for assets
  location ~ "^/assets/(.*/)*.*-[0-9a-f]{32}.*" {
    gzip_static on;
    expires     max;
    add_header  Cache-Control public;
  }
}

Now we managed to serve the cached version of our pages but we need a way to flush the cache when it needs. To achieve this we will user a Sweeper. In Rails 4 the cache sweepers are also moved to a separate gem with the observers so we need to add the it to the Gemfile:

gem 'rails-observers'

Than we need to create a file in the app/sweepers folder called article_sweeper.rb and call the expire_page method with a reference to the page we want to flush the cache for. Let's say we use a user friendly url field for the articles and the cache files will be named after that field. In this case to expire those we will need to pass the raw url string to the method:

class ArticleSweeper < ActionController::Caching::Sweeper
  observe Article

  def after_save(record)
    expire_page(articles_path)
    expire_page("/#{record.url}")
  end
end

That's all, we achieved to setup the page caching for our Rails 4 app.

Resources

actionpack-page_caching

rails-observers

PS: If you want to get updates from me please subscribe to my email list.
I hate spam as much as you do, so I won't send you anything else than Ruby/Rails related updates occasionally, and of course you can unsubcribe anytime.