Search code examples
ruby-on-railsimagecarrierwavebrowser-cache

Rails: image_tag doesn't append timestamp of image's last change (to force refresh instead of cache when loading page)


I use Carrierwave to do file uploads in my Rails app.

When replacing an already uploaded image, the browser still renders the cached original image to the user. Only after hard-refreshing (Cmd-Shift-R on Mac) it renders the new one.

I read here (Stop images from caching in Rails and browser?) that Rails would attach the image's "last updated" timestamp to the URL, but it seems it doesn't in my app:

Screenshot

Maybe this changed meanwhile (the mentioned post is from 2009)? And how would that be fixed today? Thank you.


Solution

  • image_tag uses asset_path to calculate final image path (in case it is from asset pipeline), but neither of those add cache buster timestamp.

    When image is in asset pipeline and asset digests are enabled - a checksum will be added to file name by asset compilation. For uploaded images it's a task of your image uploading library or yourself.

    For example (popular in the past) library paperclip used to save image timestamp in <attachment>_updated_at attribute and add it to generated links.

    For carrierwave it's possible to include timestamp in file name at upload like:

    class PhotoUploader < CarrierWave::Uploader::Base
      def filename
        @name ||= "#{timestamp}-#{super}" if original_filename.present? and super.present?
      end
    
      def timestamp
        var = :"@#{mounted_as}_timestamp"
        model.instance_variable_get(var) or model.instance_variable_set(var, Time.now.to_i)
      end
    end
    

    Or make it generate unique filenames by some other algorithm. Anyway using original user-provided filename in your storage is usually not a good idea from security point of view (for example someone can upload nasty_xss_hack.html).

    Other option - is to add timestamp yourself, model's updated_at.to_i is not ideal (usually model is updated more othed than the image), but will have the job done.