How to check if jQuery.ajax() request header Status is "304 Not Modified"?
jqXHR.status
usually returns 200
, even when requested header is "304 Not Modified".
ifModified:true
does not help a lot because it breaks XHR data request.
How to check if jQuery.ajax() request header Status is "304 Not Modified"?
jqXHR.status
usually returns 200
, even when requested header is "304 Not Modified".
ifModified:true
does not help a lot because it breaks XHR data request.
I have kind of implementation in https://github.com/laukstein/ajax-seo/blob/cbed03222d89f6f7e75dc49ce8882c30201bbb86/index.php#L266-274
var cache;
$.ajax({
...
beforeSend: function() {
cache = setTimeout(function() {
console.log('The content not cached jet.');
}, 300);
},
success: function(data) {
if (cache) {
clearTimeout(cache);
}
}
});
Also the headers will be cached if the response comes from cache. I took this as a chance. My server sends a unique md5 header. This has not the same effect like ?query=time(). Now on JS side verify the lastest md5 header with the current. If you receive the same MD5 header as before the response is coming from cache. I forgot all about JQuery but saw it's possible to set and read headers.
<?php
$oldMD5=filter_input(INPUT_SERVER,'HTTP_CONTENT_MD5',int);
if(!empty($oldMD5)){
header('Content-MD5: '.(1+(int)$oldMD5));
}
--------------------------------------------------------------
<script>
var oldMD5=0;
//your Ajax implementation
Ajax.setRequestHeader('Content-MD5',''+oldMD5);
var newMD5=parseInt(Ajax.getResponseHeader('Content-MD5'),10);
if(newMD5 && oldMD5<newMD5,10){
oldMD5=newMD5;
//not cached
}else{
//cached
}
This is a little bit abuse, because md5 should be a hash for to verify that the 'decoded data are the same data that were initially sent'.
Without handling cache headers manually, it is not possible. Normally, 304 responses are not made available through the XHR API:
jQuery normally doesn't know there was a 304 response, because the browser tells polite lies to JavaScript about what is actually happening over the network.
But there is good news (kind of): you can get Ajax to produce a 304 response, but only by manually setting the HTTP cache headers
If-Modified-Since
orIf-None-Match
in the request:So, you can use code like:
One fundamental difficulty is how do you know what last-modified date or ETag to send? The browser has cache information that it uses for sending requests, but it won't share that information with JavaScript. Fortunately, jQuery keeps track of the
Last-Modified
andETag
headers from Ajax responses, so you can useifModified:true
to have jQuery set those header values the next time it sends a request for that resource.Two things to note about this:
304 responses do not carry data. This is by design. The assumption is that if you have elected to use caching, you should have a copy of the data already in your cache! If getting no data from the sever is a problem (i.e., because you don't already have that data) why are you using caching? Caching should be used when you have the old data on hand and only want new data; thus, getting back no data with a 304 should not be a problem.
jQuery must have a last-modified date or an ETag (to use with
If-None-Match
) stored from a previous request. The process goes like this:First fetch: jQuery has no cache information, so it doesn't send
If-Modified-Since
orIf-None-Match
. When the response comes back, the server may announce a last-modified data or an ETag, which jQuery stores for future use.Subsequent fetches: jQuery has cache information from the last fetch and forwards that data to the server. If the resource has not changed, the Ajax request gets a 304 response. If the resource has changed, the Ajax request gets a 200 response, along with new cache information for jQuery to use for its next fetch.
jQuery does not persist cache information (e.g., in cookies) between page reloads, however. Therefore, the first fetch of a resource after a page reload will never be a 304, because jQuery has no cache information to send (i.e., we reset back to the "first fetch" case). There is no reason why jQuery couldn't persist cache information, but at present it doesn't.
The bottom line here is that you can use cache headers to get a JavaScript 304 response, but you can't access the browser's own ETag or last-modified date for a particular resource. So, the browser itself might know caching information about a resource, but your JavaScript code does not. In that case, the browser will use its cache headers to potentially get a real 304 response, but forward a 200 response to your JavaScript code, because JavaScript didn't send any cache information.
It is not possible to make JavaScript 304 requests align perfectly with actual network 304 responses, because the cache information known by your browser and the cache information known by your JavaScript code may differ in unpredictable ways. However, getting 304 requests correctly most of the time is good enough for most practical development needs.
Example
Here's a brief server example written in Node.js (but it should be simple enough to port to other langauges):
When running this server, you can load the page in your browser and perform two different tests in your browser console:
This script will always see a
200
response, even if the browser supplies aIf-Modified-Since
request header and gets a304
(which will happen all requests after the first, after the browser sees the server'sLast-Modifed
response header).By contrast, this script will always see 304 response:
The script supplies its own
If-Modified-Since
request header (two days after the server's last-modified date); it does not rely on whatever the browser supplies forIf-Modified-Since
, and therefore is allowed (per the XHR spec) to see 304 responses.Finally, this script will always see a
200
:This is because the script uses a
If-Modified-Since
that is prior to the server's last-modified date, so the server always sends a200
. The server won't send a304
because it assumes the client doesn't have a cached copy of the most recent version (i.e., the client announces that it's seen changes since Feb 12, but there was a change on Feb 13 that the client apparently hasn't seen).