How to force a web browser NOT to cache images
Asked Answered
J

20

155

Background

I am writing and using a very simple CGI-based (Perl) content management tool for two pro-bono websites. It provides the website administrator with HTML forms for events where they fill the fields (date, place, title, description, links, etc.) and save it. On that form I allow the administrator to upload an image related to the event. On the HTML page displaying the form, I am also showing a preview of the picture uploaded (HTML img tag).

The Problem

The problem happens when the administrator wants to change the picture. He would just have to hit the "browse" button, pick a new picture and press ok. And this works fine.

Once the image is uploaded, my back-end CGI handles the upload and reloads the form properly.

The problem is that the image shown does not get refreshed. The old image is still shown, even though the database holds the right image. I have narrowed it down to the fact that the IMAGE IS CACHED in the web browser. If the administrator hits the RELOAD button in Firefox/Explorer/Safari, everything gets refreshed fine and the new image just appears.

My Solution - Not Working

I am trying to control the cache by writing a HTTP Expires instruction with a date very far in the past.

Expires: Mon, 15 Sep 2003 1:00:00 GMT

Remember that I am on the administrative side and I don't really care if the pages takes a little longer to load because they are always expired.

But, this does not work either.

Notes

When uploading an image, its filename is not kept in the database. It is renamed as Image.jpg (to simply things out when using it). When replacing the existing image with a new one, the name doesn't change either. Just the content of the image file changes.

The webserver is provided by the hosting service/ISP. It uses Apache.

Question

Is there a way to force the web browser to NOT cache things from this page, not even images?

I am juggling with the option to actually "save the filename" with the database. This way, if the image is changed, the src of the IMG tag will also change. However, this requires a lot of changes throughout the site and I rather not do it if I have a better solution. Also, this will still not work if the new image uploaded has the same name (say the image is photoshopped a bit and re-uploaded).

Jany answered 24/9, 2008 at 12:19 Comment(1)
I think this question could be rework to remove all of the "now useless context" that I put in there a while back when I wrote it. I think the title is the right one, and the top answer as well. But the question has been written originally to provide a lot of room for many kind of answers and could not have expected such a simple solution. Therefore the question is a bit convoluted for people comming in there to get the answer.Jany
P
234

Armin Ronacher has the correct idea. The problem is random strings can collide. I would use:

<img src="picture.jpg?1222259157.415" alt="">

Where "1222259157.415" is the current time on the server.
Generate time by Javascript with performance.now() or by Python with time.time()

Patois answered 24/9, 2008 at 12:27 Comment(14)
One important addition is that you can never force a browser to do anything. All you can do is make friendly suggestions. It's up to the browser and the user to actually follow those suggestions. A browser is free to ignore this, or a user could override the defaults.Trifolium
Joel, you would have been better off adding that in your own answer.Patois
Just a thought, and far too late to come to the party here - but since you have control of the image changing, perhaps it would be better to rename the image as it is updated, instead of adding a query string. So: 1222259157.jpg for example, instead of picture.jpg?1222259157. That way it is updated and but re-cached upon revisit.Landgravine
@Patois some servers doesnt allow adding text after the "?". It just return a "page cannot be found". Is there any workarounds?Urien
Thank you that worked fine. Broke my neck off trying to figure out why JQUery's Ajax $.get(...) would not show up the new resource (or even actually make the call) in IE (when in all other browsers it worked). Appended the milli time something like $.get('/rawData?action=getLeftThumbId&bigImageId='+bigImageId+"&forceRefresh="+new Date().getTime()Torrens
hi I wonder will html meta 'cache-control' solve the problem?Shiff
Rather than adding the server time, which will prevent caching altogether, why not add the file's last-modified time after the '?'. That way, the image will be cached normally until it next changes.Rusert
-1 This will disable caching making this approach extremly ineffecient only suited for debbuging but not for any kind of production env.Compeer
@for3st why downvote? this is exactly what the question asked: How to force a web browser NOT to cache images. Whether this is a good idea is another question.Patrickpatrilateral
@for3st you should not downvote just because you don't like the question. Sometimes you MUST bypass cache; This is often true if you expect a resource to update very often and the resource is critical to the presentation. Sometimes you can't afford the client to get outdated resource. Ideally you only do this on request of the user(eg add a button to let user manually choose to force cache bypass) but sometimes this is more expensive when you need to send additional requests to check whether it is out of sync or not, and adding such a button is undesirable.Metabolism
@Dmitry You are kind of right, the questioner did ask the wrong question (more like, "How to handle this" instead of "How to create a specific hack"). IMHO the answer is to create immutable resources (ie. unqique URL that incorporates a hash of the data and unlimited caching time), this would be best of both worlds and is a http caching best practice. See here: developers.google.com/web/fundamentals/performance/… . However I still believe that this answer is a hack.Compeer
@for3st i agree that it's kind of a hack since you shouldn't assume that the browser will not cache your resource even if the querystring varies; the browser should be assumed to be configurable enough to cache anyway, and if the resource is meant to by dynamic the resource url should be dynamic too. Sadly doing so is significantly more complicated than having dynamic querystrings, and in almost all cases it is safe to assume dynamic querystring will be treated as an entirely new resource and bypass caching, as many php/serverside resources and forms will stop behaving correctly otherwise.Metabolism
@for3st actually on second thought you can't really call it a hack, since querystring is a part of the url, and no browser can cache varying querystring resources since forms must behave correctly on anything that can be considered a general purpose browser. It's clever but it's not a hack. Its just ugly in that if you request "a?b=c", further requests for "a" may still use the outdated resource(even though the updated version is in cache as well), and I witnessed this firsthand in chrome.Metabolism
This method I am using since 2012 and its effective. other headers wont work like they should. Even sometime CSS/Javacscript files get cached. same random string method work for this issue.Brewery
M
54

Simple fix: Attach a random query string to the image:

<img src="foo.cgi?random=323527528432525.24234" alt="">

What the HTTP RFC says:

Cache-Control: no-cache

But that doesn't work that well :)

Murray answered 24/9, 2008 at 12:20 Comment(2)
how to give Cache-Control: no-cache in normal html <img> tag?Booth
Does it need to be come in response header?Booth
I
38

I use PHP's file modified time function, for example:

echo <img  src='Images/image.png?" . filemtime('Images/image.png') . "'  />";

If you change the image then the new image is used rather than the cached one, due to having a different modified timestamp.

Intensifier answered 17/9, 2012 at 4:4 Comment(4)
Great tip. You have both the caching and the image re-sent to the client if its modified time changes (overwritten or changed).Acceptance
see here for another option #56589350Celebrity
Perfect ! Just a extra tip: <img src="images/image.png?<?php echo filemtime('images/image.jpg')?>">Meteorograph
@RicaGurgel thanks for the above, I tried adding it to the php file as below to get the latest image from the server <img class="weather-cam-box alignleft" src="<?php echo '/cameraimages/webcam.jpg'.filemtime('/cameraimages/webcam.jpg' ); ?>" alt="" width="100%" height="764" /> it doesnt work for somereason,Dowden
E
19

I would use:

<img src="picture.jpg?20130910043254">

where "20130910043254" is the modification time of the file.

When uploading an image, its filename is not kept in the database. It is renamed as Image.jpg (to simply things out when using it). When replacing the existing image with a new one, the name doesn't change either. Just the content of the image file changes.

I think there are two types of simple solutions: 1) those which come to mind first (straightforward solutions, because they are easy to come up with), 2) those which you end up with after thinking things over (because they are easy to use). Apparently, you won't always benefit if you chose to think things over. But the second options is rather underestimated, I believe. Just think why php is so popular ;)

Elaina answered 10/9, 2013 at 1:33 Comment(3)
+1 I think it's a much better idea than other answers because using the las modification time, your server will not try to give the same image multiple times to the same browser when nothing have changed in the image. There is a little overhead to extract the last write time of the image each time there is a request but for a large image, it's better than having to returns the image each time and when the image change, the user will have the new one. Thank you for this nice little addition.Plier
Well, one might store modification time along with the path to image in database. So the overhead might be even less significant. On the other hand if these are images which are part of source code we are talking about, one might cache their modification times too. By generating a script with modification times of images (e.g. images.php). This script must be regenerated each commit and eliminates overhead of determining modification times of files.Elaina
@x-yori this is why I am opting to store this updated field in the database rather than running filemtime #56589350Celebrity
S
17

use Class="NO-CACHE"

sample html:

<div>
    <img class="NO-CACHE" src="images/img1.jpg" />
    <img class="NO-CACHE" src="images/imgLogo.jpg" />
</div>

jQuery:

    $(document).ready(function ()
    {           
        $('.NO-CACHE').attr('src',function () { return $(this).attr('src') + "?a=" + Math.random() });
    });

javascript:

var nods = document.getElementsByClassName('NO-CACHE');
for (var i = 0; i < nods.length; i++)
{
    nods[i].attributes['src'].value += "?a=" + Math.random();
}

Result: src="images/img1.jpg" => src="images/img1.jpg?a=0.08749723793963926"

Singlehearted answered 30/12, 2017 at 17:32 Comment(3)
Simple, elegant, and doesn't require server side rendering. NICE!Inveterate
Cool, this is working with a data url too. But from where this is coming NO-CACHE is a special class ? an hack in <img> element ?Etherege
@Etherege the class-name is irrelevant. this answer works because the src-value will always have a new random number appendend eg. "?a=12332191242384". The browser has most likely never seen the exact url and will not find it in the cacheCamembert
S
7

You may write a proxy script for serving images - that's a bit more of work though. Something likes this:

HTML:

<img src="image.php?img=imageFile.jpg&some-random-number-262376" />

Script:

// PHP
if( isset( $_GET['img'] ) && is_file( IMG_PATH . $_GET['img'] ) ) {

  // read contents
  $f = open( IMG_PATH . $_GET['img'] );
  $img = $f.read();
  $f.close();

  // no-cache headers - complete set
  // these copied from [php.net/header][1], tested myself - works
  header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Some time in the past
  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); 
  header("Cache-Control: no-store, no-cache, must-revalidate"); 
  header("Cache-Control: post-check=0, pre-check=0", false); 
  header("Pragma: no-cache"); 

  // image related headers
  header('Accept-Ranges: bytes');
  header('Content-Length: '.strlen( $img )); // How many bytes we're going to send
  header('Content-Type: image/jpeg'); // or image/png etc

  // actual image
  echo $img;
  exit();
}

Actually either no-cache headers or random number at image src should be sufficient, but since we want to be bullet proof..

Sergu answered 24/9, 2008 at 12:19 Comment(2)
Yours is a good solution, except that Pragma is not a response header.Throughput
@Piskvor: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32 seems to say otherwise.Felicitasfelicitate
C
7

I checked all the answers around the web and the best one seemed to be: (actually it isn't)

<img src="image.png?cache=none">

at first.

However, if you add cache=none parameter (which is static "none" word), it doesn't effect anything, browser still loads from cache.

Solution to this problem was:

<img src="image.png?nocache=<?php echo time(); ?>">

where you basically add unix timestamp to make the parameter dynamic and no cache, it worked.

However, my problem was a little different: I was loading on the fly generated php chart image, and controlling the page with $_GET parameters. I wanted the image to be read from cache when the URL GET parameter stays the same, and do not cache when the GET parameters change.

To solve this problem, I needed to hash $_GET but since it is array here is the solution:

$chart_hash = md5(implode('-', $_GET));
echo "<img src='/images/mychart.png?hash=$chart_hash'>";

Edit:

Although the above solution works just fine, sometimes you want to serve the cached version UNTIL the file is changed. (with the above solution, it disables the cache for that image completely) So, to serve cached image from browser UNTIL there is a change in the image file use:

echo "<img src='/images/mychart.png?hash=" . filemtime('mychart.png') . "'>";

filemtime() gets file modification time.

Cowen answered 19/11, 2015 at 2:39 Comment(2)
The filemtime solution is better also because md5 takes a lot of processing power.Addicted
Spent days trying to get Chromium based app to stop caching images. The ?nocache with time echo solved the issue. Thank you!Annmaria
D
6

I'm a NEW Coder, but here's what I came up with, to stop the Browser from caching and holding onto my webcam views:

<meta Http-Equiv="Cache" content="no-cache">
<meta Http-Equiv="Pragma-Control" content="no-cache">
<meta Http-Equiv="Cache-directive" Content="no-cache">
<meta Http-Equiv="Pragma-directive" Content="no-cache">
<meta Http-Equiv="Cache-Control" Content="no-cache">
<meta Http-Equiv="Pragma" Content="no-cache">
<meta Http-Equiv="Expires" Content="0">
<meta Http-Equiv="Pragma-directive: no-cache">
<meta Http-Equiv="Cache-directive: no-cache">

Not sure what works on what Browser, but it does work for some: IE: Works when webpage is refreshed and when website is revisited (without a refresh). CHROME: Works only when webpage is refreshed (even after a revisit). SAFARI and iPad: Doesn't work, I have to clear the History & Web Data.

Any Ideas on SAFARI/ iPad?

Despiteful answered 31/3, 2016 at 16:40 Comment(0)
C
4

When uploading an image, its filename is not kept in the database. It is renamed as Image.jpg (to simply things out when using it).

Change this, and you've fixed your problem. I use timestamps, as with the solutions proposed above: Image-<timestamp>.jpg

Presumably, whatever problems you're avoiding by keeping the same filename for the image can be overcome, but you don't say what they are.

Craigie answered 25/9, 2008 at 2:9 Comment(0)
H
3

You must use a unique filename(s). Like this

<img src="cars.png?1287361287" alt="">

But this technique means high server usage and bandwidth wastage. Instead, you should use the version number or date. Example:

<img src="cars.png?2020-02-18" alt="">

But you want it to never serve image from cache. For this, if the page does not use page cache, it is possible with PHP or server side.

<img src="cars.png?<?php echo time();?>" alt="">

However, it is still not effective. Reason: Browser cache ... The last but most effective method is Native JAVASCRIPT. This simple code finds all images with a "NO-CACHE" class and makes the images almost unique. Put this between script tags.

var items = document.querySelectorAll("img.NO-CACHE");
for (var i = items.length; i--;) {
    var img = items[i];
    img.src = img.src + '?' + Date.now();
}

USAGE

<img class="NO-CACHE" src="https://upload.wikimedia.org/wikipedia/commons/6/6a/JavaScript-logo.png" alt="">

RESULT(s) Like This

https://example.com/image.png?1582018163634
Homonym answered 18/2, 2020 at 9:35 Comment(0)
R
2

Your problem is that despite the Expires: header, your browser is re-using its in-memory copy of the image from before it was updated, rather than even checking its cache.

I had a very similar situation uploading product images in the admin backend for a store-like site, and in my case I decided the best option was to use javascript to force an image refresh, without using any of the URL-modifying techniques other people have already mentioned here. Instead, I put the image URL into a hidden IFRAME, called location.reload(true) on the IFRAME's window, and then replaced my image on the page. This forces a refresh of the image, not just on the page I'm on, but also on any later pages I visit - without either client or server having to remember any URL querystring or fragment identifier parameters.

I posted some code to do this in my answer here.

Rusert answered 15/3, 2014 at 21:30 Comment(0)
N
2

From my point of view, disable images caching is a bad idea. At all.

The root problem here is - how to force browser to update image, when it has been updated on a server side.

Again, from my personal point of view, the best solution is to disable direct access to images. Instead access images via server-side filter/servlet/other similar tools/services.

In my case it's a rest service, that returns image and attaches ETag in response. The service keeps hash of all files, if file is changed, hash is updated. It works perfectly in all modern browsers. Yes, it takes time to implement it, but it is worth it.

The only exception - are favicons. For some reasons, it does not work. I could not force browser to update its cache from server side. ETags, Cache Control, Expires, Pragma headers, nothing helped.

In this case, adding some random/version parameter into url, it seems, is the only solution.

Numerable answered 25/11, 2015 at 11:50 Comment(2)
One reason you might want to disable image caching is to allow a reference to a dynamically generated image which updates periodically.Lantha
@Manngo, have you read my comment carefully?Numerable
K
2

Add a time stamp <img src="picture.jpg?t=<?php echo time();?>">

will always give your file a random number at the end and stop it caching

Karl answered 10/1, 2018 at 15:28 Comment(1)
Worth noting that implementing this solution puts stress on the server per request and won't necessarily work since the browser can cache the php page which contains the time generated at the time of initial request which gets cached. This would only work reliably if the page as well had a dynamic query string.Metabolism
A
1

With the potential for badly behaved transparent proxies in between you and the client, the only way to totally guarantee that images will not be cached is to give them a unique uri, something like tagging a timestamp on as a query string or as part of the path.

If that timestamp corresponds to the last update time of the image, then you can cache when you need to and serve the new image at just the right time.

Astrionics answered 24/9, 2008 at 12:24 Comment(0)
C
1

I assume original question regards images stored with some text info. So, if you have access to a text context when generating src=... url, consider store/use CRC32 of image bytes instead of meaningless random or time stamp. Then, if the page with plenty of images is displaying, only updated images will be reloaded. Eventually, if CRC storing is impossible, it can be computed and appended to the url at runtime.

Cards answered 21/2, 2010 at 15:32 Comment(1)
Or, use the image's last-modified time, instead of a CRC, even better!Rusert
M
1

Ideally, you should add a button/keybinding/menu to each webpage with an option to synchronize content.

To do so, you would keep track of resources that may need to be synchronized, and either use xhr to probe the images with a dynamic querystring, or create an image at runtime with src using a dynamic querystring. Then use a broadcasting mechanism to notify all components of the webpages that are using the resource to update to use the resource with a dynamic querystring appended to its url.

A naive example looks like this:

Normally, the image is displayed and cached, but if the user pressed the button, an xhr request is sent to the resource with a time querystring appended to it; since the time can be assumed to be different on each press, it will make sure that the browser will bypass cache since it can't tell whether the resource is dynamically generated on the server side based on the query, or if it is a static resource that ignores query.

The result is that you can avoid having all your users bombard you with resource requests all the time, but at the same time, allow a mechanism for users to update their resources if they suspect they are out of sync.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="mobile-web-app-capable" content="yes" />        
    <title>Resource Synchronization Test</title>
    <script>
function sync() {
    var xhr = new XMLHttpRequest;
    xhr.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {            
            var images = document.getElementsByClassName("depends-on-resource");

            for (var i = 0; i < images.length; ++i) {
                var image = images[i];
                if (image.getAttribute('data-resource-name') == 'resource.bmp') {
                    image.src = 'resource.bmp?i=' + new Date().getTime();                
                }
            }
        }
    }
    xhr.open('GET', 'resource.bmp', true);
    xhr.send();
}
    </script>
  </head>
  <body>
    <img class="depends-on-resource" data-resource-name="resource.bmp" src="resource.bmp"></img>
    <button onclick="sync()">sync</button>
  </body>
</html>
Metabolism answered 17/10, 2018 at 16:28 Comment(0)
G
1

All the Answers are valid as it works fine. But with that, the browser also creates another file in the cache every time it loads that image with a different URL. So instead of changing the URL by adding some query params to it.

So, what we can do is we can update the browser cache using cache.put

caches.open('YOUR_CACHE_NAME').then(cache => {
  const url = 'URL_OF_IMAGE_TO_UPDATE'
  fetch(url).then(res => {
    cache.put(url, res.clone())
  })
})

cache.put updates the cache with a new response.

for more: https://developer.mozilla.org/en-US/docs/Web/API/Cache/put

Gyron answered 2/2, 2022 at 11:6 Comment(0)
D
1

I've found Chrome specifically tries to get clever with the URL arguments solution on images. That method to avoid cache only works some of the time. The most reliable solution I've found is to add both a URL argument (E.g. time stamp or file version) AND also change the capitalisation of the image file extension in the URL.

<img src="picture.jpg">

becomes

<img src="picture.JPG?t=current_time">
Drumstick answered 26/5, 2022 at 11:2 Comment(0)
Q
-1

I made a PHP script that automatically appends the timestamps on all images and also on links. You just need to include this script in your pages. Enjoy!

http://alv90.altervista.org/how-to-force-the-browser-not-to-cache-images/

Quod answered 30/12, 2019 at 14:28 Comment(0)
M
-2

Best solution is to provide current time at the end of the source href like <img src="www.abc.com/123.png?t=current_time">

this will remove the chances of referencing the already cache image. To get the recent time one can use performance.now() function in jQuery or javascript.

Manager answered 3/5, 2021 at 11:19 Comment(1)
What is the point of posting this answer? It is the same (even down to the function you are recommending be used to get the time) as the accepted answer from 13 years ago.Fixity

© 2022 - 2024 — McMap. All rights reserved.