ETags in Rails 4

Ademar Tutor | July 15, 2013

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:

Here's a breakdown of how ETags works in Rails:

First Request:

  • 1. Render the entire response body

  • 2. Create an ETag by doing a MD5 hash on the entire response body:

    header['Etag'] = Digest::MD5.hexdigest(body)
                      

  • 3. Send back to the client the response body and ETag included in the response

  • 4. If ETags match then the response body is not included in the response, only the 304 response code

    • Second Request on the same page:

      • 1. Render the entire response body

      • 2. Create ETag by doing a MD5 has on the entire response body:

        header['Etag'] = Digest::MD5.hexdigest(body)
                          

      • 3. Compares ETag with what was sent over and what it generated

      • 4. 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'

Create Custom ETag

Here's a breakdown of how ETags works in Rails:

First Request:

  • 1. Create an Etag from the model

  • 2. Renders the entire response body

  • 3. Send back to the client the response body and ETag

    • Second Request

      • 1. Fetches the model

      • 2. Create an ETag from the model

      • 3. Compare the generated ETag from the one received from client

      • 4. If ETags match then the response body is not included in the response, only the 304 response code

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, curernt_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