Improve caching for Rails proxied assets on Cloudflare
I recently ported the Jammed Rails app to Cloudflare, and noticed that the ActiveStorage
assets were not being cached properly. I did some digging, and found this to be because Cloudflare issues BYPASS cf-cache-status headers for assets, due to Rails issuing a cookie with the assets. I couldn’t find any Rails help or documentation on this, so I thought I’d write it up.
Proxy Rails assets to origin
I have images uploaded to the Rails app using ActiveStorage
, and I want to cache them on Cloudflare. The assets in Rails are setup using the proxy method, so that they are served from Cloudflare, using the Rails app as origin, instead of simply redirecting to the source file (e.g. S3). This is done by adding the following to config/application.rb
:
1
config.active_storage.resolve_model_to_route = :rails_storage_proxy
Now, when I visit the site, I can see that the assets are being served proxied and not redirected, but they are still not being cached by the CDN. Cloudflare is issuing a cf-cache-status: BYPASS
header, which means that the asset is not being cached, and is always being served directly from the origin.
This is because Rails is issuing a set-cookie
header with the assets, and Cloudflare is not caching assets with cookies. This is expected behaviour, and is documented in the Cloudflare docs as: “Cloudflare also sets BYPASS when your origin web server sends cookies in the response header.”.
Adding the cache rule
To force Cloudflare to cache these assets, we need add the cache rule.
I went to the Cloudflare dashboard, and selected the domain I wanted to add the cache rule to. I then went to the “Caching” tab, and selected “Cache Rules” from the sidebar. This is luckily available to all plan levels, and not just to my Pro account.
Once here, I then clicked “Add Cache Rule” and added the following:
The key here is to match the URL pattern, and to set the cache level for our Rails proxied assets.
rails/active_storage/representations/proxy
Forcing the edge cache
Even with the cache rule in place, the assets were still not being cached. I found that I needed to force the edge cache to store, by setting the edge TTL:
Given that the assets are not changing, I set the TTL to 1 year, which is the maximum allowed, but maybe start with 1 hour or 1 day, and then increase it once you are happy that it is working.
Rails generates a new URL for each asset and each variant, so the cache rule will match each time, and the edge cache will be forced to store the asset. The long TTL will mean that the edge cache will be used for a long time, and the origin will not be hit for the same asset.
Andy Callaghan makes Jammed - booking software for music rehearsal studios and recording studios