304 or 200?

Posted in development, firefox, web at 10:20 am by wingerz

When you make a request over HTTP, you can get a number of status codes returned (like 200 (OK) and 404 (not found)). One such code is 304, which tells the client that the page requested was not modified, meaning that the client can use the local cached version.

Unfortunately, when you make a XMLHttpRequest in Firefox (allowing you to make a HTTP request without refreshing the page) and the return status set by the web server is 304, the XMLHttpRequest.status property is set to 200. In most cases this is fine because it doesn’t matter whether the page came from a webserver or from the local cache – in either case, the content is available for use.

In our case we want to be able to query our server about a particular entity and only take additional action running some SPARQL queries) if the status code is 200. When our server returns a 304, we won’t do anything. But since Firefox only gives a 200 in this case, we’ve had to add a custom HTTP header.

I found some mention of this problem elsewhere. In my opinion, if a webserver returns a 304, that should get bubbled up to the client somehow since it has meaning beyond 200, so I filed a bug report. Sounds like not everyone agrees with me.


  1. Lee Feigenbaum said,

    July 1, 2006 at 11:59 am

    I’m pretty curious as to *why* you want to take different action for a 200 response vs. a 304 response. That sounds to me like itmight be breaking an abstraction boundary.

    (i.e. while I am surprised that the XmlHttpRequest object doesn’t have access to or doesn’t pass along the raw status code from the server, semantically a client shouldn’t care at all where the response comes from — that’s the entire point of a caching system.)

    Have you tested this in any other browsers to see if their XHR implementations do the same thing?

    What I picture happening is XHR implementation requests the Firefox HTTP stack to retrieve a page – so the FF HTTP stack is basically acting as the web server for the XHR client — it also functions as a client to the actual web server which returns the 304. The FF HTTP stack in its role as server takes the 304 response and says hey, I have this in my cache, so I’ll just give it back to my client from there. So then it can pass along a 200 OK response since everything is A-OK.

    (It also passes along the custom header you added since it doesn’t know diddly about it, so it thinks it might be important.)


  2. Cy said,

    July 1, 2006 at 2:40 pm

    So, let me get this straight:

    1. XMLHttpRequest asks Firefox to ask the server for something.
    2. Firefox checks its cache, finds a local copy, and sends the server a conditional GET.
    3. The server responds with a 304 and no content.
    4. Firefox grabs the cached content and hands it to the XMLHttpRequest object with a 200 code.

    Did I get that right? If so, I can see why this is not a settled debate. From one perspective, they don’t care where the content came from, so the XMLHttpRequest object is supposed to get it from wherever and hand it to you with a success code. From your perspective, it matters not just that you get the content, but whether it has been updated or not.

    If I understand the mechanism correctly, the solution seems to be (exactly as you stated) that at step 4 above, Firefox grabs the cached content and hands it to the XMLHttpRequest object with a 304 code. Then, those who don’t care where it came from can use the content without any further steps, and those who do care can check the status code for 200 or 304.

    IMO, one necessary refinement would be to have a function .succeeded() for users who don’t care where the content comes from, e.g. “if (request.succeeded() == true)” rather than “if (request.status == 200)”, so it would catch 200, 203, 304, etc. Unfortunately, this would require a lot of people to modify their existing codebase, which might be why there is opposition to this idea.

  3. wingerz said,

    July 1, 2006 at 5:41 pm

    Lee: I vaguely recall reading somewhere that Opera gives you a 304 in this case. we can talk about this more next week, but we’re basically using it to allow the client to poll the server for changes on a particular entity on the server, only refreshing content (via a SPARQL query) if that entity has changed.

  4. Lee Feigenbaum said,

    July 2, 2006 at 3:00 am

    As you said we’ll talk more about it, but it sounds to me like the correct way to do it is to always issue the SPARQL queries and make use of a cache at that level that will interpret 304s and return you the cached results of the query… maybe piggybacking on HTTP caching and the SPARQL protocol GET bindings?

    If that doesn’t work, I’d say it’s a weakness in the SPARQL protocol, and the attempt to discriminate between 200 and 304 is a hack to get around that weakness, rather than a clean design.


  5. kellegous said,

    July 2, 2006 at 8:50 pm

    I posted this at Elias’ blog, but I’ll double post it here too.

    Though it isn’t an official specification, the draft spec for XHR does weigh in on this. Though it also seems to suggest (as I read it) that you can get the 304 back if you manually setup the conditional request with setRequestHeader(…)

    If the author performed a conditional GET request (setting the relevant headers using setRequestHeader()) and the resulting response is a 304 the UA MUST return a 304 and it is expected the page author can deal with that. Otherwise, if the UA performed a conditional GET request and the response is a 304 the UA MUST return a 200 (using the relevant cached file).

Leave a Comment