I have narrowed down a very nasty Android bug.
If I turn on Expires headers on our content delivery network Android AJAX stops working (may affect CSS well). Android XMLHttpRequest responds with a status code zero:
03-20 12:55:33.564: WARN/browser(238): Console: AJAX loading:http://cdn.mobilizejs.com/releases/trunk/templates/wordpress.html http://cdn.mobilizejs.com/releases/trunk/js/mobilize.wordpress.min.js?ver=3.1:1
03-20 12:55:33.663: WARN/browser(238): Console: done http://cdn.mobilizejs.com/releases/trunk/js/mobilize.wordpress.min.js?ver=3.1:1
03-20 12:55:33.743: WARN/browser(238): Console: Could not AJAX url:http://cdn.mobilizejs.com/releases/trunk/templates/wordpress.html got status:0 http://cdn.mobilizejs.com/releases/trunk/js/mobilize.wordpress.min.js?ver=3.1:1
03-20 12:55:33.853: WARN/browser(238): Console: Status text: http://cdn.mobilizejs.com/releases/trunk/js/mobilize.wordpress.min.js?ver=3.1:1
03-20 12:55:33.864: WARN/browser(238): Console: Payload: http://cdn.mobilizejs.com/releases/trunk/js/mobilize.wordpress.min.js?ver=3.1:
- It is a definitely some sort of error code (without information, not even internal Androidd info in LogCat).
- It only occures with Expires header
So I suspect something is wrong in Expires headers…
Headers serving attempt #1:
Expires: 21 Mar 2011 11:37:52 GMT
Date: Sun, 20 Mar 2011 11:37:52 GMT
Cache-Control: no-cache
Note that Cache-control: no-cache is set automatically by App Engine which we use to host file. This works, though from App Engine logs it is obvious the browser is re-fetching all resources on every request.
Adding weekday to Expires header doesn’t seem to work. Google App Engine writes it.
If you add only Expires and no Cache-Control, Google App Engine defaults to Cache-Control: no-cache.
If you add only Cache-Control header, no Expires, it seems that the client caches the result. However, in this case, Android AJAX calls stop working.
These seem to trigger the bug in Android emulator (XHR status code 0 for AJAX call):
Content-type: text/html
Access-Control-Allow-Origin: *
Cache-Control: max-age=86400
Date: Sun, 20 Mar 2011 11:56:39 GMT
Server: Google Frontend
As a temporary workaround, I just make this particular problematic resource re-fetched on every HTTP request on Android phones by inserting a random token into URL:
var url = mobilize.toFullCDNURL(mobilize.cdnOptions.template);
if(navigator.userAgent.toLowerCase().indexOf("android") >= 0) {
mobilize.log("Avoiding Android cache control problem for AJAX");
// Android AJAX + cache is busted
// http://mobilizejs.com/2011/03/20/android-webkit-xhr-status-code-0-and-expires-headers/
url += "?android-buster=" + Math.random();
}
console.log("Loading mobile template from URL:" + url);
Logs tell this seem to be effective and other resources besides this AJAX call are no longer loaded:
2011-03-20 05:22:36.966 /releases/trunk/templates/wordpress.html?android-buster=0.7062135764234672 200 181ms 298cpu_ms 251api_cpu_ms 0kb Mozilla/5.0 2011-03-20 05:21:09.978 /releases/trunk/templates/wordpress.html?android-buster=0.42276109455817257 200 77ms 298cpu_ms 251api_cpu_ms 0kb Mozilla/5.0
2011-03-20 05:20:42.687 /releases/trunk/templates/wordpress.html?android-buster=0.8590675375212578 200 85ms 298cpu_ms 251api_cpu_ms 0kb Mozilla/5.0
Note that this does not solve the problem How to cache AJAX responses on Android at all, it justs works around this particular case.
More info