34

i'm issuing a request using MSXML's XmlHttpRequest object:

IXMLHttpRequest http = new XmlHttpRequest();
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.send();

And the send succeeds, and i get my xml data.

Except that XmlHttpRequest didn't actually hit the network (i can see there no actual http request issued). And Process Monitor shows the file is actually being served from my cache:

enter image description here

So i want to instruct the XmlHttpRequest user agent that any cached content older than 0 seconds is too old. The standards way to do this is to add a request header:

Cache-Control: max-age=0

to the send request:

http = new XmlHttpRequest();
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "max-age=0");
http.send();

And the send succeeds, and i get my xml data.

Except that XmlHttpRequest didn't actually hit the network (i can see there no actual http request issued). And Process Monitor shows the file is actually being served from my cache.

So what is wrong? Is max-age not doing what i think it does?

From RFC 2616 - Hypertext Transfer Protocol, Part 14: Header Field Definitions:

Other directives allow a user agent to modify the basic expiration mechanism. These directives MAY be specified on a request:

max-age
Indicates that the client is willing to accept a response whose age is no greater than the specified time in seconds. Unless max- stale directive is also included, the client is not willing to accept a stale response.

Which exactly what i want.

Is Cache-Control: max-age=0 not exactly what i want, or is MSXML's XmlHttpRequest object buggy?

Update One

This is the MSXML XmlHttpRequest COM object:

  • CLSID: {88d96a0a-f192-11d4-a65f-0040963251e5}
  • ProgID: Msxml2.XMLHTTP.6.0

Update Two

The max-age directive is added by the client for all cache's to adhere to. From RFC:

The Cache-Control general-header field is used to specify directives that MUST be obeyed by all caching mechanisms along the request/response chain. The directives specify behavior intended to prevent caches from adversely interfering with the request or response. These directives typically override the default caching algorithms. Cache directives are unidirectional in that the presence of a directive in a request does not imply that the same directive is to be given in the response.

Max-age is not for the server; it makes no sense for a server. It is intended for all caching systems between the user and the server.

Update Three

From W3C XmlHttpRequest:

If the user agent implements a HTTP cache it should respect Cache-Control request headers set by the setRequestHeader() (e.g., Cache-Control: no-cache bypasses the cache). It must not send Cache-Control or Pragma request headers automatically unless the end user explicitly requests such behavior (e.g. by reloading the page).

Following their example, i tried using the no-cache directive:

http = new XmlHttpRequest();
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "no-cache");
http.send();

And the XmlHttpRequest client still services requests completely from the cache, without querying the server at all.

The W3C says that if there is a cache, it must honor Cache-Control if it is set through setRequestHeader. Microsoft's XmlHttpRequest doesn't seem to honor that requirement.

Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219

10 Answers10

25

Unfortunately the XMLHttpRequest object was designed this way, because it is based on WinInet. Also, it is not recommend to be used from the server side. You should use ServerXMLHttpRequest, which has the same functionality, but depends on WinHTTP instead. See the FAQ for more information. A description from the ServerXMLHttp documentation states that:

The HTTP client stack offers longer uptimes. WinInet features that are not critical for server applications, such as URL caching, auto-discovery of proxy servers, HTTP/1.1 chunking, offline support, and support for Gopher and FTP protocols are not included in the new HTTP subset.

This means that rather than using XmlHttpRequest:

IXMLHTTPRequest http = CreateComObject("Msxml2.XMLHTTP.6.0");     http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "max-age=0");
http.send();

you can use ServerXmlHttpRequest:

IXMLHTTPRequest http = CreateComObject("Msxml2.ServerXMLHTTP");
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "max-age=0");
http.send();

or WinHttpRequest:

IWinHttpRequest http = CreateComObject("WinHttp.WinHttpRequest.5.1");
http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml", False, "", "");
http.setRequestHeader("Cache-Control", "max-age=0");
http.send();
Ian Boyd
  • 246,734
  • 253
  • 869
  • 1,219
Garett
  • 16,632
  • 5
  • 55
  • 63
  • You're right. i just tested this using `ServerXmlHttpRequest` and `WinHttpRequest`. Those two do no caching whatsoever. While `XmlHttpRequest` does perform caching - it doesn't follow the W3C standards for that requests to bypass that cache. +1 and Accepted. – Ian Boyd Apr 12 '11 at 18:19
  • I tried using both WinHttp.WinHttpRequest.5.1 and MSXML2.ServerXMLHTTP.6.0, but It didn't work for me... can that be a server setting or something else? – thiagoleite Dec 01 '11 at 12:35
  • @thiagoleite What didn't work for you? What OS are you using? Did you receive and error? – Garett Dec 01 '11 at 17:12
  • @Garret Its a webfarm of 5 Windows Server 2008 64, IIS7. I created a proxy in ASP Classic to consume a rss feed, it just makes a request and parse the xml. I get no error at all, just that when i make an update in the feed, my proxy won't get the updated information. I read somewhere that ServerXmlHttpRequest has a bug regarding caching, but cant remember where i read it. – thiagoleite Dec 01 '11 at 17:29
  • @thiagoleite Hi, this may require a new question in order to solicit more feed back. It may well be a bug, but I would also like to see more details, including snippets of the code you are using. – Garett Dec 14 '11 at 15:01
8

I found that using If-None-Match header, specifing a value that does not match the ETag of the last request will work.

Eg:

req.open("GET", url, false);
req.setRequestHeader("If-None-Match", "\"doesnt-match-anything\"");
req.send();

This might or might not require that the responses include an ETag. (I only tried it with a service that includes an ETag value in each response.)

Nat Ritmeyer
  • 5,634
  • 8
  • 45
  • 58
J. Mullaney
  • 81
  • 1
  • 1
1

My quick and dirty workaround at a standard Windows client was
- Internet Options
- General
- Browsing history Settings
- Check for newer Versions of stored pages:
tickle "(x) Every time I visit the Webpage"
Now my Msxml2.XMLHTTP.x.0 Object don't use the Cache anymore ...

anonymous
  • 11
  • 1
1

Could you append a bogus parameter on the end of your URI that changes with each request?

http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml?requestID=42", False, "", "");
  • 4
    Problem with that is that it doesn't bypass the cache, it simply issues a separate request; filling my cache with thousands copies of the same resource. i'd like to know how to follow the cache rules. – Ian Boyd Mar 08 '11 at 16:58
1

I use this for a keep-alive session and it works great.
The trick is to use the header "If-Modified-Since" with a value newer that the one cached by the browser.

g_AjaxObj.onreadystatechange = function() { if(g_AjaxObj.readyState === 4) { AjaxOnComplete_("KeepAlive"); }};
g_AjaxObj.open('GET', URL, true);
g_AjaxObj.setRequestHeader("If-Modified-Since", new Date().toUTCString());
g_AjaxObj.send(null);
Bo Persson
  • 90,663
  • 31
  • 146
  • 203
tranzitwww
  • 11
  • 1
  • 1
    `If-Modified-Since` the opposite of what i want. Adding `If-Modified-Since` allows the server to return `304 Not Modified`. i want to bypass the cache - not work with it – Ian Boyd Nov 07 '11 at 16:32
0

Downside of that is that you flood the cache with multiple copies of the same content. It might be a hack around buggy http agents - but a real solution is to work with caching mechanisms, rather than against them. –

I agree that this is not ideal and not really a solution, but Mozilla actually recommend this as a workaround, so I figure it mustn't be too terrible - https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Using_XMLHttpRequest

Besides, I was tearing my hair out trying to solve this. I've had to rely on my users to clear their browser (which they keep forgetting to do). So this is a godsend for me!

0

Try sending 'cache-control: private' as a header. This worked for me:

var request = new XMLHttpRequest();
request.open("GET", 'http://myurl.com' , false); 

request.setRequestHeader("cache-control", "private");

I'm writing a HTML & Javascript app for Windows 8 where and both no-cache and max-age are ignored. For me the above works just fine.

I wasn't familiar with the header so did a little digging on cache-control: private...

Indicates that all or part of the response message is intended for a single user and MUST NOT be cached by a shared cache, such as a proxy server.

From What is Cache-Control: private? and http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

So, basically, this will never create a cache entry and therefore doesn't add cache entries that we know are superfluous, as with a 'cache-buster' random number parameter.

Community
  • 1
  • 1
Matt
  • 1,471
  • 1
  • 12
  • 12
0

This was driving me crazy. This SO thread came closest to providing an answer. Unfortunately during testing none of them actually worked for me. The only solution I found which tested to work correctly was setting:

Header Pragma: no-cache

I hope it saves others with IE headaches.

BTW this is StackOverflow thread is great to shed light on the difference between Pragma and Cache-control: Difference between Pragma and Cache-control headers?

Community
  • 1
  • 1
0

This header is for the server, and as the browser doesnt event do any request, its useless.

An easy trick is to load the page like this :

http.open("GET", "http://www.bankofcanada.ca/stat/fx-xml.xml?"+Math.random(), False, "", "");
jujule
  • 11,125
  • 3
  • 42
  • 63
  • Problem with that is that it doesn't bypass the cache, it simply issues a separate request; filling my cache with thousands copies of the same resource. i'd like to know how to follow the cache rules. – Ian Boyd Mar 08 '11 at 16:58
  • the server only sends an 'etag' header in the response, so the cache is pure client-side ? you can force IE not to cache in options->internet options->temp files->always check new file – jujule Mar 09 '11 at 08:40
  • This isn't from ie, it's a native application using `XmlHttpRequest`. – Ian Boyd Mar 17 '11 at 14:41
  • IMHO native xmlhttpapps use the IE cache settings. as they use IE proxy – jujule Mar 17 '11 at 15:08
  • i cannot be changing the cache policy on every machine that runs my application (that's just not being a good program). And besides, the W3C dictates that if i specify a `Cache-Control` header in my request, the XmlHttpRequest implementation is supposed to honor it. – Ian Boyd Apr 12 '11 at 18:09
0

For old msxml library, I use random generated value for uri adress eg :

http://youlink?mysession=random_number

wojtek

wojtek
  • 1
  • 1
    Downside of that is that you flood the cache with multiple copies of the same content. It might be a hack around buggy http agents - but a real solution is to work *with* caching mechanisms, rather than against them. – Ian Boyd Sep 19 '11 at 15:45