Reputation: 5200
I have a script 3rd party websites are using: /assets/script.js
. For obvious reasons, I can't ask them to change the link every time I deploy to point to the latest fingerprinted version of the script. I got a few caching issues where users still see old versions of /script.js
. Are there any ways to make the cache go away directly for script.js
instead of script-9dc5afea3571ba2a883a72b0da0bb623.js
?
More Information: Rails on Passenger + Nginx. Looking for ways to serve the script.js
file instead if the finger-printed file and invalidate the cache on every deployment.
I thought about adding ETags based on the deployment git revision, but have no idea how to do this. Nginx has no built in ETags support. There are unsupported old third party modules that do this. I can use add_header Etag="something"
for this, but how do I add the git version there.
Any other ideas and options?
Thanks!
Upvotes: 11
Views: 690
Reputation: 501
You want a non fingerprinted asset url for third party websites. For example: assets/public_api.js
There have been plugins or gems that excluded specified assets from fingerprinting. However rails has changed the pre-compilation process in a way that also creates non-fingerprinted files. Therefore this isn't a problem. More info here.
How to make sure your clients are loading the latest deployed script, when the asset is not fingerprinted?
I would suggest the solution youTube uses to expose their API. Basically all your assets/public_api.js does, it injects another script tag into the dom. That injected one loads the actual API code. Now your assets/public_api.js becomes assets/public_api.js.erb and looks something like this:
var tag = document.createElement('script');
tag.src = "<%=asset_path('/assets/javascripts/acctual_api')%>";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
Please note how tag.src is set to the current fingerprinted path to /assets/javascripts/acctual_api. This way your users will always get the latest compiled acctual_api script.
How to update the ETAG for the assets/public_api.js?
I suppose you use Capistrano or similar recipe based deployment solution. Maybe you could add a deployment step that updates the server config file before its restarted. It should just update:
add_header Etag="update_me_on_deploy"
Please note that you should still use versioned (assets/public_api.0.js) public scripts even with this approach.
Upvotes: 1
Reputation: 3195
Well you can remove the fingerprint of the file :
asset_path('script.js', :digest => false)
hope it helps
Or you can use this gem if you want : https://github.com/spohlenz/digestion
But : The Rails asset pipeline now compiles asset files both with and without digests.
So after you generate your assets usually you got script.js?xxxxx and script.js into your public/assets folder.
Upvotes: 1
Reputation: 1599
If you are using Capistrano, you could write a task that copies the script from Public/assets to another directory within Public (i.e. Public/scripts) after your assets are precompiled.
Upvotes: 1
Reputation: 4230
nginx is able to generate etags in the latest version: http://nginx.org/en/docs/http/ngx_http_core_module.html#etag
I've also seen the configuration below here: https://serverfault.com/questions/426260/nginx-cache-control
location /static {
alias /opt/static/blog/;
access_log off;
etags on;
etag_hash on;
etag_hash_method md5;
expires 1d;
add_header Pragma "public";
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
Upvotes: 3
Reputation: 1965
What I'm using for updating assets is:
Increment config.assets.version
in config/application.rb
like
#Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.1'
bundle exec rake assets:precompile RAILS_ENV=production RAILS_GROUPS=assets
App restart, empty webserver cache if any
Upvotes: 1
Reputation: 787
I recommend using an ETag. Add an ETag header to your response http://en.wikipedia.org/wiki/HTTP_ETag
Set the ETag header to a different, unique string for each version of your script. This will make sure browsers get a new version of the script whenever you deploy a new version .
Upvotes: 3
Reputation: 7540
Following up on the ETag suggestion, you might find this gem useful: bust_rails_etags. It allows you to set a key on each deployment which is used in the generation of ETags, that way, your ETags will change (and so the cached script will be invalidated) every time your app is deployed. The author uses the example of Heroku release numbers as a key that changes on each deploy.
Upvotes: 1
Reputation: 955
If you have a script with a name that is part of your public interface then you need to start versioning this script explicitly, and keeping old versions around for older clients.
e.g. /assets/script.1.0.js, /assets/script.1.1.js etc
The key part is that you need to be keeping the old ones around, and the code doesn't change without the name changing explicitly. The Rails asset pipeline can't do this for you, since there's usually only the very latest version of the script kept current.
As with all public interfaces, you will need to spend more time on managing this process than you would for an internal-only script.
Upvotes: 9