14

I want to ensure that data I request via an AJAX call is fresh and not cached. Therefor I send the header Cache-Control: no-cache

But my Chrome Version 33 overrides this header with Cache-Control: max-age=0 if the user presses F5.

Example. Put a test.html on your webserver with the contents

<script>
    var xhr = new XMLHttpRequest;
    xhr.open('GET', 'test.html');
    xhr.setRequestHeader('Cache-Control', 'no-cache');
    xhr.send();
</script>

In the chrome debugger on the network tab I see the test.html AJAX call. Status code 200. Now press F5 to reload the page. There is the max-age: 0, and status code 304 Not Modified.

Firefox shows a similar behavior. Intead of just overwriting the request header it modifies it to Cache-Control: no-cache, max-age=0 on F5.

Can I suppress this?

sod
  • 3,804
  • 5
  • 22
  • 28

5 Answers5

27

Using a query string for cache control isn't your best option nowadays for multiple reasons, and (only) a few are mentioned in this answer. He even explains the new standard method of version control. Though if you just want to be able to set your request headers, the right way to do it is:

    // via Cache-Control header:
    xhr.setRequestHeader("Cache-Control", "no-cache, no-store, max-age=0");
    
    // fallbacks for IE and older browsers:
    xhr.setRequestHeader("Expires", "Tue, 01 Jan 1980 1:00:00 GMT");
    xhr.setRequestHeader("Pragma", "no-cache"); //Edit: I noticed this is required for Chrome some time ago... forgot to mention here

Hope this helps anyone in the future.

hewiefreeman
  • 1,137
  • 1
  • 14
  • 31
  • 1
    Notes: `expires` is not required if `max-age` is set ([source](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expires)); `post-check=0` and `pre-check=0` are not required ([source](https://www.drupal.org/node/2646280)); `must-revalidate` cannot be used client-side ([source](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control)). – thdoan Jul 25 '18 at 22:54
  • They might not be required, but there are older browsers that will ignore some of the other headers in which case they will kick in to do the job (I've done testing on this myself with crossbrowsertesting.com). Though I can't vouch for `must-revalidate` as I didn't know much about it and just threw it in for overkill. – hewiefreeman Jul 25 '18 at 23:36
24

An alternative would be to append a unique number to the url.

<script>
    var xhr = new XMLHttpRequest;
    xhr.open('GET', 'test.html?_=' + new Date().getTime());
    //xhr.setRequestHeader('Cache-Control', 'no-cache');
    xhr.send();
</script>

timestamp isn't quite unique, but it should be unique enough for your usecase.

Franklin Yu
  • 8,920
  • 6
  • 43
  • 57
Kevin B
  • 94,570
  • 16
  • 163
  • 180
  • 22
    That it not true. [Cache-Control is both a response header and a request header.](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4) – wadim Oct 30 '14 at 17:26
  • @wadim So Chrome and Firefox do not respect RFC 2616 (now 7234)? – Franklin Yu May 29 '17 at 02:53
  • 1
    just a note, this answer is a community wiki, so if you feel it doesn't adequately answer the question feel free to update it. I don't have time atm to revisit it. – Kevin B May 29 '17 at 02:54
  • In some situation it is preferable to have the same url for the request to the same API end point. I think this answer still not cover that case??? – chanp Jun 28 '17 at 15:17
  • 1
    @chanp correct, it doesn't cover that case. that case would only be solvable with headers, but chrome v33 overrides some of them... so... yea, goodluck :) – Kevin B Jun 28 '17 at 15:19
  • @KevinB I happen not to read the whole introduction. Pardon me. – chanp Jun 28 '17 at 15:22
  • Please don't use this method as it defeats the purpose of the Proxying and CDN caching. A better configuration of your server and your client will be your best choice. – M. Gara Mar 18 '18 at 13:34
  • This doesn't cover the whole situation because if you access the url without the unique number that page will remain outdated (cached). – Bruno de Oliveira Nov 17 '20 at 20:00
0

I tried (and failed) some sort of randomization to the URL, but it didn't work because the file I was accessing (.json) was being cached as well.

My solution was to add a timestamp to the call to the json file name (similar approach to ones above, slightly modified). This worked perfectly for me (code snippet below).

doSomething('files/data.json?nocache=' + (new Date()).getTime(), function(text){...

I'm very new at all of this so I'm sure there are reasons this isn't a standard/correct solution, but it worked for me.

jake_n
  • 1
  • 1
0

Removing the element and adding a new element with jQuery (JS also, I think) works for me in Chrome.

// Wordpress Context, selectedImage is an object from the Media Selector Dialog
const imageID = selectedImage.id 
const imageURL = selectedImage.url   

// This is a div with the img as an only child
const logoImageContainer = $('#q1nv0-preview-image-container')
let clone = $(logoImageContainer).find('img').clone()
// In the cloned img object I change the src to the new image      
clone.removeAttr("src").attr('src', imageURL)

// Also I have to remove this in wordpress context:
clone.removeAttr("srcset")
// the div is cleared of the old image      
$('#q1nv0-preview-image-container').empty()
// the new image is added to the div
$(logoImageContainer).prepend(clone)
-1
http.setRequestHeader("Cache-Control", "no-cache, no-store, must-revalidate");
Kobi
  • 2,494
  • 15
  • 30