I stumbled upon some curious browser behavior yesterday, and would like to share it here, lest it save someone a few hours of debugging. This story is about JSON requests, HTTP header based context switching, and browser caching.
The scenario
I was working on a live search plugin for WordPress; one that displays matching results below the search box as you type. The PHP part of the plugin was designed to hook into WordPress’ search facility and make it output JSON, for the Javascript part of the plugin to consume and display. The hook was designed to run only if a search was initiated by an AJAX request. To detect such a request, and implement the context switch, it made use of the “X-Requested-With” header sent by the MooTools framework:
function json_requested() { return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' && isset($_SERVER['HTTP_ACCEPT']) && $_SERVER['HTTP_ACCEPT'] == 'application/json'; } if($this->json_requested()) { // Output the query as JSON add_action('wp', array($this, 'output_search_results_as_json')); }
The bug
This worked quite well, but my client began to notice an intermittent bug. On occasion, when making a non-AJAX request for the search results page, then leaving the search results page by following a link, then clicking the browser’s back button to return, the browser would display (Internet Explorer 6) or begin to download (Firefox 3) the JSON version of the search results, instead of the expected HTML response. When I heard the word “intermittent,” I smelled a race condition, opened up my trusty Charles web debugging proxy, and set to work trying to recreate the error.
The problem
What I found was that the last response to return for a given URL would be the one that the browser would cache, no matter what kind of response you were expecting given a certain set of request headers.
Here is what Charles says about the order of server responses with the bug absent. You’re looking at a set of four AJAX requests initiated by the Javascript application, and responded to by the server, followed by a non-AJAX request initiated when the user clicked submit on the search form.
Next, what we have is Charles’ report at the time the bug was discovered. Three AJAX requests receive responses, and while a fourth one is in progress, the user submits the search form. As we can see, the fourth JSON request returns after the HTML search page returns. The last response across the finish line, is the one that the browser will remember for the URL /?s=food.
Solutions
Turns out, you have at least two simple solutions in this case:
- Cancel all of the AJAX requests at the time the form is submitted
- Use a different URL according to the response you expect
In my case, I opted to make use of the cancel() method on my instance of the MooTools Request class to ensure that any AJAX requests for JSON would be killed before a request for the HTML version of the search results page even got rolling.
Tags: ajax, bugs, javascript, php








