Safari back button and web service response behaviour

On a recent responsive website project, we encountered some puzzling behaviour in Safari on OS X and iOS when making asynchronous JavaScript requests to a web service. When navigating forward to a page (i.e. from the navigation menu, a hyperlink, or even entering the URL directly in the browser) things worked as expected. However, going back with the browser back button would cause the same page to fail to load. We’d get seemingly random “type undefined” JavaScript errors, despite a success response code from our web service requests.

We eventually discovered that Safari loads web service responses from cache when going back to a previously visited page. In theory that’s a good thing – rather than making another request to the web service, the page’s static and dynamic content can be loaded from cache, and the page displays almost instantly when going back. The issue we encountered was because we were making POST requests to the same URL, with parameters that determined the type of request defined in the POST body (a SOAP web service request).

The details for this can be seen using the Safari Web Inspector, although the steps might not be overly intuitive for the uninitated:

Safari Web Inspector POST Response

A POST response in Safari Web Inspector

  1. Choose Timelines in the top tabs, then load (or reload) the page (you won’t see any information if the page has already been loaded in the browser)
  2. Choose Network Requests
  3. Find the endpoint being requested under Resources
  4. Switch to the Response to see the response body from the web request

Once we saw the response coming back from our web service requests, things became clear. The service we were consuming exposed only a single endpoint URL, and Safari doesn’t know which cached response to use when you go back to a page that makes multiple AJAX requests to the same URL.

We set a break point inside the “success” method for an AJAX call, and when we navigated forward to a page the response body was what we expect. If we go back to the same page, when we hit the break point again, the response body was something completely different. Since the web service response didn’t contain the expected content, we’d either have weird content showing up in the UI, or (more often) some sort of script error. This explained why the exact errors we different – they varied by the web service requests that the page required.

To get Safari to recognize the cached response for a specific request, we added a parameter to the request URL in the AJAX call, so that each type of web service request would have a unique URL:

$.ajax({
       url: "/somesite/_vti_bin/lists.asmx?r=menu",
       type: "POST",
       contentType: "text/xml; charset='utf-8'",
       dataType: "xml",
       data: soapEnv,
       error: function(xhr, status, error) {
       console.error(xhr.responseText);
    },
    success: function(data, status, xhr) {
        ...
    }
});

Note the ?r=menu addition to the URL. In our case, this allowed Safari to see that this response if for the “menu” request, so when going back and getting the now cached response, we get the response body that we were expecting. As long as the parameter is unique for the request parameter, Safari is be able to line up the correct cached response. The response body was still cached, so we maintain the performance advantage of Safari’s caching behaviour, but were ensured to get the data we expected.

Leave a Reply

Your email address will not be published.

top