The Hazards of Relying upon Browser Quirks

While many web developers find subtle browser behaviors baffling, often browser developers are bewildered by web content. Yesterday, we ran into an interesting site compatibility problem that occurs in the latest internal version of IE9.

The site in question is a popular site which uses a Flash applet as a major component of the site. Upon attempting to log in to the site, an error message is displayed in the applet. The problem does not reproduce in IE8 or any other browser; only in internal IE9 builds built after the beta was released. Upon further examination, we uncovered the root cause of the problem.

The server is sending a bunch of headers that say “no matter what you do, don’t write this file to the cache.

POST https://site/folder/xmlrpc/?method=authenticate HTTP/1.1

HTTP/1.0 200 OK
Date: Tue, 21 Sep 2010 23:02:54 GMT
Content-Type: text/xml
Content-Length: 2837
Cache-Control: no-cache, no-store, no-transform, must-revalidate, max-age=-1
Pragma: no-cache, no-store
Expires: -1
Connection: close

In particular, both the bolded green lines clearly communicate that the server does not want the file cached. (The negative value for the max-age directive is illegal, but the rest of the directives are fine).

The problem is that the site's Flash applet appears to fail in IE if this file isn't cached (apparently a common complaint for Flash developers).

So, the immediate question is why did this ever work in IE8 and what broke in IE9?

After some investigation, it became clear that the site is deliberately structuring its cache headers to make it appear that the file would not be cached, while actually allowing IE to create a cache file. A bold claim, to be sure, but let me explain.

First, you'd expect that the no-store and no-cache directives would prevent caching of this HTTPS content. However, note that these are HTTP/1.1 cache control directives, and the response was delivered as a HTTP/1.0 response. Prior to our very latest IE9 builds, IE would only respect the max-age (and deprecated pre-check/post-check) tokens in a response delivered as HTTP/1.0. So, in this case, the no-cache and no-store directives were ignored.

Next, you'd expect that the pre-HTTP/1.1 Pragma header would prevent caching. However, IE only respects this header for HTTPS and only if the value is exactly and only no-cache. In this case, because the site has added an additional token to the header, IE ignores the Pragma directive.

Our latest internal IE9 builds have begun respecting all of the Cache-Control tokens regardless of the response's HTTP version, which led to the site breakage.

Now, could it be possible that this site just blindly happened upon these quirks, by accident? Possibly, but for one additional clue: The site in question uses browser sniffing to sniff for the Internet Explorer User-Agent string, and only if it finds IE does it change the response to be HTTP/1.0. For all other browsers, it returns a HTTP/1.1 response. It's obvious that the site owners are relying upon the quirks in legacy IE code to allow caching, despite sending headers that would appear to prevent it.

Clever readers might protest: "perhaps the site is trying to prevent caching on an intermediary while allowing caching in Internet Explorer?" That would make sense if any caching intermediary could "see" this response, but because it's delivered over HTTPS, that will not happen.

Our outreach team has contacted the site in question to request that they follow best practices for caching.


Comments (8)
  1. Adam Barth says:

    Fantastic post.  🙂

  2. ken says:

    I'm trying to understand this, because there's several things not adding up here. Eric's alot smarter than I am so I must be missing something, but I've had to deal with these headers before and I remember it being a nightmare (I starred this post so that I can double-check the implementation at work).

    My question is this: does the site in question with the headers given above, function in modern browsers? I would think so, being a popular site as you say. Otherwise, it seems to imply that not a single web browser properly handles these headers, because if they did then Flash wouldn't work, right?

  3. EricLaw [MSFT] says:

    I suspect the story in non-IE browsers is that the NPAPI Flash plugin used in non-IE browsers is not dependent upon a cache file, and instead simply gives the content directly to the applet. Why they don't do the same in IE is unfortunately a question I cannot answer, since I don't have either a Flash building tool, or their source.

  4. ken says:

    So, it doesn't sound like they're doing anything nefarious or intentionally misleading. I think they simply don't know about the Pragma bug. Seriously, you can't expect everyone to know every IE 6 bug in existence — and especially to know of *two* different bugs. They're sniffing IE 6 and sending HTTP 1.0 due to the one bug, you can't blame them for that.

    And technically, aren't they already following "best practices for caching"? Your statement of "Our outreach team has contacted the site in question to request that they follow best practices for caching" sounds pompous to me, as it seems to say that the site is doing something wrong, when in reality, it's bugs in IE 6 that are the issue here, not the site. I think a more accurate resolution to this would be for your outreach team to educate the site about the myriad IE 6 bugs that lead to unexpected behavior, point them to this post, and then humbly request that they alter their code to make yet another one-off exception for IE 6.

    Lastly, searching for any combination of "Internet Explorer pragma cache control" or something similar such as "how to prevent caching in Internet Explorer" yields the following KB article, regardless of search engine used, as the top result:…/234067 If you notice, nowhere in that document are these IE 6 bugs mentioned (interestingly, there's several other IE bugs relating to caching mentioned in the sidebar, but not these 2 bugs). Therefore, how can one realistically expect developers to know of these bugs? Are these 2 bugs even documented in your KB (I would think you would have linked them, if so)?

  5. EricLaw [MSFT] says:

    @Ken: No, sending invalid caching directives is not "following best practices for caching." Similarly, sending "no-store" directives for a file that you require to be stored is not a best practice.

    I never said they were trying to be "nefarious"– I merely pointed out that they carefully constructed their response so that it would be cached despite the presence of many directives that would indicate otherwise. I have no idea what "IE 6 bugs" you're asking about– if you're implying that it's a bug that the incorrect Pragma directive is ignored, I'm afraid you're incorrect. Pragma was defined as a request header in HTTP/1.0, and only for legacy compatibility is it supported as a response header at all.

  6. jayp says:

    These sorts of posts are great.

  7. Yuhong Bao says:

    EricLaw: I think by "bug" they mean "quirk".

  8. Goyuix says:

    I have seen many plugins and 3rd party apps (poor Adobe, both Flash and Reader in particular) behave very badly when the following code is set server side on SSL connections: Response.Cache.SetCacheability(HttpCacheability.NoCache) – which seems to correspond with what this post is describing. It impacts Silverlight too – something about how IE doesn't cache the file and then can't hand it off to the plugin or something, I never really dug deep enough honestly.

    Lesson: Don't set NoCache on SSL sessions in IE or all sorts of things start going south.

Comments are closed.

Skip to main content