Forms, CSRF authenticity token and fragment caching in Rails


You have a fragment cache like the following:

-cache ['v2', user.username, signed_in?, viewing_self?] do
    = user.endorsements.count
  - user.works.each do |work|
    %li.work = work.title
      = form_tag(user_endorsements_path(@user)) do
        = hidden_field_tag :endorsed_work, work
        = link_to 'endorse', user_endorsements_path(user)

When the above haml code is compiled, rails inserts a unique csrf authenticity token (session based) in the form so when you submit it, it can rule out cross-site forgery attempts.

<form accept-charset="UTF-8" action="/users/6522/endorsements" method="post">

<div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓">

<input name="authenticity_token" type="hidden" value="VY13wlC2rgGccbkxyvm7Z1WX4LKH+71vzIj+8Um0QO8="></div>

However, when your form is inside a cache fragment, as the example above demonstrates, the first time the html fragment is cached the authenticity token is part of it. Now if your sessions expires and you login again, you will be served the cache fragment that contains the original csrf authenticity token value and if you try to submit the form rails will end session(log you out) and return a

403 Forbidden


405 Method Not Allowed



A simple solution is to put the following code in your application.js file so it replaces all authenticity tokens on a page with the default one in the header:

meta content="authenticity_token" name="csrf-param" />

meta content="VY13wlC2rgGccbkxyvm7Z1WX4LKH+71vzIj+8Um0QO8=" name="csrf-token" />

in application.js put(JQuery version):


now all old csrf tokens in the cache fragments will be replaced by the correct value from the current session

2 Responses
Add your response


What if you want to do page/action caching?

over 1 year ago ·

This just made my day, thanks!

over 1 year ago ·