The HTTP specification says one thing, Safari does another.

Web developers are reporting that the Safari Web browser in iOS 6 is breaking the applications they're writing thanks to aggressive caching. Discussion of the issue on Stack Overflow explains that Safari is remembering the server responses to certain requests and then reusing those responses, even though a new response should be requested every time.

Web requests can take many forms, but there are two main kinds: GET and POST. As a general rule, POST should be used when you expect there to be a side-effect of some kind; GET should be used when there is no side-effect. So, for example, a search for a book on Amazon should use a GET; since searching doesn't change any state (it doesn't place any orders, or change your billing information, or anything like that), GET is appropriate. The final "Buy" button in the checkout, however, should use POST, because it has a big side-effect: your credit card gets billed, a book is shipped out, and so on.

Because of this fundamental difference, the two have different default rules about caching. GET requests can generally be cached; the result of a GET is broadly expected to be the same each time, and since there's no side-effect from a GET it doesn't actually matter if a browser sends the request to a server, or just uses a cached response. POST, on the other hand, generally can't be cached. If you want to know what the result of a POST is, you have to ask the server again, because the response may very well be different—to use the Amazon example again, your credit card might refuse the transaction the second time around, or the book may now be out of stock.

POST responses are not completely uncachable, but according to the HTTP specification the responses must explicitly indicate that they can be cached. The responses to GET requests don't require this explicit indication; instead, there are generic rules that are applied.

Evidence suggests that Safari on iOS 6 is ignoring these rules. Developers are using POST for operations with side-effects, but Safari is caching the results and not giving applications the correct response. The result is that the apps are prone to breaking.

A number of solutions have been proposed at Stack Overflow. When developers have control of both the server and the client, they can configure the server to explicitly indicate that responses shouldn't be cached. This shouldn't be necessary, and isn't required for any other browser, but Safari will pay attention if caching is suppressed. When developers only control the client, they can add a timestamp or similar to each POST they make, to ensure that no two posts are identical. This assumes that the server doesn't mind receiving a timestamp it neither wants nor asked for.

Whether this is a bug or a deliberate attempt by Apple to improve perceived performance of its browser is as yet unclear. We've asked Apple for comment, but as yet have heard nothing back.

With the current state of web applications (which is not anything like what people were imagining while writing the standards), I think caching anything without at least doing a 304 check is really a pretty bad idea. HTML bandwidth is negligible now compared to pipes, and caching creates a whole host of issues.

My guess is that this is being done to further...let's say, "creatively improve" the results of battery life testing. As Anandtech said in their iPhone 4S review:

Quote:

The move to iOS 5 in particular hurt our web browser test as it cached so much of the content of each page that our cellular results now closely mirror our WiFi results on the iPhone 4/4S. There's still a bit of a penalty to be paid over 3G, but not nearly as much as it should be in the real world.

I wonder, could aggressive caching of things that normally shouldn't be cached also affect Javascript performance? Say, just by pulling results of a request from a cache rather than actually running the script?

With the current state of web applications (which is not anything like what people were imagining while writing the standards), I think caching anything without at least doing a 304 check is really a pretty bad idea. HTML bandwidth is negligible now compared to pipes, and caching creates a whole host of issues.

But on device power budget isn't neglible.

I don't think people realize that the "caching" mobile devices do is MUCH more involved then just recording the HTML received from the server.

It's much more common for caching to be at a lower level, so instead of the HTML engine having to process the cached info, the lower level processes just display it.

It's kinda like how iOS saves a screen grab of your app so when you re-launch it it displays that, while actually starting the app in the background. It gives the impression of a much faster loading time.

I believe that's caching of GETs, which is, broadly speaking, acceptable. jQuery's ajax() method uses GET by default, in most circumstances, and the snippet of code posted doesn't change it to POST.

One of the commenters in the linked post wrote: I had this same issue with Android. Android (for whatever reason) like to cache PUTs and POSTs, at least for the browser that is on my phone (HTC G2) and the emulator. This was causing my ajax PUT calls to be cached on the client, and they were not actually getting to the server.

Everything we do on our site includes a timestamp or uuid for each request. I kinda thought this was common practice as older browsers and such have a habit of caching requests too.

It seems to me most of the ajax libraries I've used include a uuid or timestamp by default.

Edit: Just did a little digging, and it seems most of them just add timestamps to the GET requests to prevent caching. When I do an AJAX post request, it doesn't include a timestamp. I guess it is assumed that POSTs will not be cached.

I'm also finding iOS 6 can break standalone webapps with HTML5 audio. Just visit http://www.apple.com/html5/showcase/audio/ from iOS 6 Safari and click the play button, then "Add to Home Screen" and try again. No audio the second time. JavaScript can fully interact with the audio, but no sound.

I'm also finding iOS 6 can break standalone webapps with HTML5 audio. Just visit http://www.apple.com/html5/showcase/audio/ from iOS 6 Safari and click the play button, then "Add to Home Screen" and try again. No audio the second time. JavaScript can fully interact with the audio, but no sound.

That definitely makes this look like a bug regardless of whether it was originally deemed a feature by Apple.

This assumes that the server doesn't mind receiving a timestamp it neither wants nor asked for.

Any web application that can't handle random, unrequested data knocking at its server side API door has no business on the open internet. Or on your private intranet, either.

Ever heard about that DoS attack involving specially crafted GET/ POST variables that the developer isn't expecting? It uses deliberate hash collision to take advantage of weak hash algo design, throwing thousands of unexpected GET/ POST vars at the server all with similar hashes, so as to deliberately force the expense of running the hash algo to grow at O(n²) — thereby enabling a single workstation to bring down an entire server. One obvious fix for this attack involves blocking or ignoring submissions with unexpected GET/ POST variables...

Ever heard about that DoS attack involving specially crafted GET/ POST variables that the developer isn't expecting? It uses deliberate hash collision to take advantage of weak hash algo design, throwing thousands of unexpected GET/ POST vars at the server all with similar hashes, so as to deliberately force the expense of running the hash algo to grow at O(n²) — thereby enabling a single workstation to bring down an entire server. One obvious fix for this attack involves blocking or ignoring submissions with unexpected GET/ POST variables...

Yes I've heard of hash collisions, but if your filter is implemented using the same server-side language interpreter you're using for your application, you're already hosed the moment the request comes in. (More explicitly, the DoS happens before your code can begin to execute.) Blocking this for GET is easy enough if you have the ability to limit the length of accepted URLs to something reasonably large for your API. I'm not sure what you'd do when coding or deploying an application to protect yourself from hash colliding POST requests.

This type of attack has been known for a while and the best answer is better hash generation in the language interpreter, which hopefully everyone who codes interpreters has implemented. It's also completely irrelevant to the need to have your app not do something stupid if it receives something as simple as a single cache busting URL argument. Sure, you may want to reject the request outright if you're paranoid about invalid input attacks, but has anyone here ever seen or coded an API like that? Have you ever added something to a URL and seen a web server go hostile? In my experience, "validate any expected data and drop anything else" is the standard most APIs aim to achieve.

Everything is configurable on Android. All you have to do is download the source. Make the change. Compile. Etc.

Sure. Because I should have to learn Java, download the source, figure out how to run a compiler, etc, to fix a bug the BROWSER SHOULDN'T HAVE ANYWAY.

Would it be too much to ask that the devs stick a checkbox in a settings screen? Something like "I'm a braindead idiot that wants to think the intarwebz is working faster" vs. "I want my browser to work correctly, please."

As a (part time) web-developer myself I've never really trusted default caching behaviours; my view is that you haven't specified cache-control etc., then you've waived control of the behaviour. It's therefore always better to be a bit paranoid and either always refuse caching on POST requests, or use the actual results of the request to determine whether it is cacheable or not.

It's still a pretty stupid bug, and one that needs to be fixed, but I think it may serve as a nice wake-up call for API developers that if you want to control how something is cached, then send the right cache-control headers!

As a (part time) web-developer myself I've never really trusted default caching behaviours; my view is that you haven't specified cache-control etc., then you've waived control of the behaviour. It's therefore always better to be a bit paranoid and either always refuse caching on POST requests, or use the actual results of the request to determine whether it is cacheable or not.

It's still a pretty stupid bug, and one that needs to be fixed, but I think it may serve as a nice wake-up call for API developers that if you want to control how something is cached, then send the right cache-control headers!

A bug could just as well ignore the cache-control header.

Some enterprise networks plainly ignore the cache-control header for GETs. They configure the network to use overaggressive caching.

Tangential to this, I find it unbelievable that on mobile browsers its either so hard or impossible to close the browser, do other stuff for a while, then come back and have your Web pages at the ready. No, they have to reload...

And now you're telling me they're caching most of the stuff? Why can't I tick a "don't auto reload" option? Kills battery, sucks when your reception is spotty, and makes absolutely no sense forI speed.

This has been my story on iOS and Android, over multiple versions and multiple browsers. Just awful.

Tangential to this, I find it unbelievable that on mobile browsers its either so hard or impossible to close the browser, do other stuff for a while, then come back and have your Web pages at the ready. No, they have to reload...

Maybe because phones don't have a Core processor, 4gb ram and unlimited battery?

This assumes that the server doesn't mind receiving a timestamp it neither wants nor asked for.

Any web application that can't handle random, unrequested data knocking at its server side API door has no business on the open internet. Or on your private intranet, either.

It does not break the web application, it breaks the experience for that user.

I think I saw this error and I was unable to figure it out. I imagine all the web apps deal with it in a similar way that mine did if they are not especially programmed for it. (Mine sends users to a default error page telling them something broke)

Lastly, every web app can take random data. There is usually at least six or seven attacks every day from the new script kiddies. I also see allot of suspicious malformed stuff coming from people using Synapse (http://synapse.apache.org/).