ChatGPT解决这个技术问题 Extra ChatGPT

How to force a web browser NOT to cache images

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

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.

e
epochwolf

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()


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.
Joel, you would have been better off adding that in your own answer.
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.
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.
The last comment by Doin have to be upvoted! Smart caching is important indeed, why someone would have to download over and over the same file?
A
Armin Ronacher

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 :)


how to give Cache-Control: no-cache in normal html tag?
Does it need to be come in response header?
b
bfavaretto

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.


Great tip. You have both the caching and the image re-sent to the client if its modified time changes (overwritten or changed).
see here for another option stackoverflow.com/questions/56588850/…
Absolute nailed it. Great tip mate.
Perfect ! Just a extra tip:
@RicaGurgel thanks for the above, I tried adding it to the php file as below to get the latest image from the server it doesnt work for somereason,
x
x-yuri

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 ;)


+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.
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.
@x-yori this is why I am opting to store this updated field in the database rather than running filemtime stackoverflow.com/questions/56588850/…
A
Aref Rostamkhani

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"


Simple, elegant, and doesn't require server side rendering. NICE!
2
2 revs, 2 users 99% Mindeh

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


Yours is a good solution, except that Pragma is not a response header.
@Piskvor: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32 seems to say otherwise.
T
Tarik

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.


The filemtime solution is better also because md5 takes a lot of processing power.
Spent days trying to get Chromium based app to stop caching images. The ?nocache with time echo solved the issue. Thank you!
A
AmbroseChapel

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


S
Sensei James

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?


A
Ali Han

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

C
Community

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.


A
Alexandr

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.


One reason you might want to disable image caching is to allow a reference to a dynamically generated image which updates periodically.
@Manngo, have you read my comment carefully?
B
BritishSam

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


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.
u
user7375

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.


j
jbw

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.


Or, use the image's last-modified time, instead of a CRC, even better!
D
Dmitry

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>

B
Bucket

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">

R
Ravi Singh

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


A
Andrew

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/


M
Minal Chauhan

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.


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.