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.
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
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.”.
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.
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