Rails 5 has introduced some changes to the way HTTP caching works.
ETags are by default now set to
weak mode in Rails 5. This is because Rails does not do byte-to-byte checking for equality which is comparing changes in both the headers and response body.
Weak Etags only compares the response body for differences, which is also the default behaviour in earlier versions of Rails.
Assuming we have a Users controller and an expensive rendering action on the index action which we want to call render only if the users have been updated.
We can implement a conditional get using
stale? like so:
1 if stale?(etag: @users, last_modified: @users.maximum(:updated_at) 2 respond_to do |format| 3 format.html 4 end 5 end
Assuming we made a curl request to the endpoint /users:
1 curl -i http://localhost:3000/users
It will return a 200 response:
1 HTTP/1.1 200 OK 2 X-Frame-Options: SAMEORIGIN 3 X-XSS-Protection: 1; mode=block 4 X-Content-Type-Options: nosniff 5 ETag: W/"8e49d1a448037c58d93ea3a00d6edf87"
Note that the ETag now has a
W/ prefix to indicate its a weak etag, which is the default in Rails 5.
If we now use the ETag value and make a second curl request with the header
If-None-Match set to the same ETag value we should get a 304 response.
1 curl -i -H 'If-None-Match: "8e49d1a448037c58d93ea3a00d6edf87"' http://localhost:3000/users
However, the above will keep returning 200 as it is not matching the response. You need to specify the entire ETag value like so:
1 curl -i -H 'If-None-Match: W/"8e49d1a448037c58d93ea3a00d6edf87"' http://localhost:3000/users
To use strong etags (without the W/ prefix), you can change the
etag parameter in the
1 if stale?(strong_etag: @users, last_modified: @users.maximum(:updated_at)) 2 respond_to do |format| 3 format.html 4 end 5 end
Stay curious and keep hacking!