ETags in Rails 4


What are ETags?

ETag or entity tag is part of HTTP that is assigned by a web server to a specific version of a resource found at URL. In other words, ETAG is a key we use to determine whether a page has changed.

ETags in action

Here’s a diagram on how etags works:

How ETags work?

Here’s a breakdown of how ETags works in Rails

First Request:

  • Render the entire response body
  • Create an ETag by doing a MD5 hash on the entire response body:
header['Etag'] = Digest::MD5.hexdigest(body)
  • Send back to the client the response body and ETag included in the response
  • If ETags match then the response body is not included in the response, only the 304 response code

Second Request on the same page:

  • Render the entire response body
  • Create ETag by doing a MD5 has on the entire response body:
header['Etag'] = Digest::MD5.hexdigest(body)
  • Compares ETag with what was sent over and what it generated
  • If ETags match then the response body is not included in the response, only the 304 response code

Custom ETags

Every time the server gets a request from client, it re-renders the page to generate the Etag, which is not that efficient. The solution for this is to create your own custom Etags with fresh_when method.

class ProfilesController < ApplicationController
  def show
    @profile = Profile.find(params[:id])
    fresh_when(@profile) # Sets the etag equal to the cache key
  end
end

fresh_when method creates a MD5 hash of the model

headers['Etag'] = Digest::MD5.hexdigest(@item.cache_key)

The cachekey is a combination of the model name, id and updatedat attribute.

'<model name>/<id>-<updated_at>'
'profile/2-201322415000'

Declarative ETags

Etags can be generated with multiple arguments. Which may something like this:

class ProfilesController < ApplicationController
  def show
    @profile = Profile.find(params[:id])
    fresh_when([@profile, current_user.id])
  end
end

If we have multiple actions in our controller using ETags with multiple arguments, our code may end up looking like this:

class ProfilesController < ApplicationController
  def show
    @profile = Profile.find(params[:id])
    fresh_when([@profile, curernt_user.id])
  end

  def edit
    @profile = Profile.find(params[:id])
    fresh_when([@profile, curernt_user.id])
  end 
end

To make our code more DRY, we’ll use declarative ETags. Declarative ETags allows us to declare an ETag on top of our controller:

class ProfilesController < ApplicationController
  etag { current_user.id }
  def show
    @profile = Profile.find(params[:id])
    fresh_when(@profile)
  end

  def edit
    @profile = Profile.find(params[:id])
    fresh_when(@profile)
  end 
end

That’s it! Hopefully you’ve learned how to use ETags on your Rails 4 applications