2010 update:
Lo, the Web Performance Advent Calendar hath moved
Dec 22 This post is part of the 2009 performance advent calendar experiment. Stay tuned for the articles to come - only 2 to go!
Some time ago there was a post on YUIBlog highlighting the findings of Wayne Shea and Tenni Theurer on the caching behavior of the iPhone. Curious if things have changed several iPhone OS updates later, I ran some experiments with OS3 and OS3.1.
Highlights
- iPhone will not cache components bigger than 15K (was 25K in the previous experiment)
- total cache is about 1.5MB (was 500K)
- short-term memory cache will store components up to 1941K provided the component size does not divide by 4
- powering off the device still clears all the cache, Expires headers still important
- not sure since when, but closing all tabs clears the cache too
- consider HTML5 application cache to improve cacheability and provide offline experience – components in the application cache can be bigger than 15K and stay cached even after you clear Safari's cache from the Settings app (or clear it any other way)
15 is the new 25
Previous experiment showed that the iPhone will not cache components bigger that 25K, but this number is now down to 15K. And this is ungzipped size, meaning if you have for example a 20K JavaScript and your server sends it gzipped down to 10K, it will not be cached by the iPhone.
So minification can definitely help you here. Minification may not be as beneficial as gzipping in desktop browsers, but it may be the difference between a cache hit or miss in the iPhone. Minify, then gzip. And, in general, try to keep your component sizes low - that always helps in any browser.
Total cache size
First off, how was the experiment run? I simply tailed the access log of a server, in order to monitor which components get requested:
$ tail -f ~/logs/http/access.log | grep "XXX.YYY.ZZZ.000"
Where XXX.YYY.ZZZ.000 is the IP address. You can also replace that with just "iPhone" if you think no one else is hitting this site with an iPhone.
Then I requested components with different sizes and look at what the log tail says.
Once it was clear that 15K is the maximum components size, the next question was how many of those 15K components can be cached before iPhone runs out of space allocated for caching? And the result was 105. Attempting to cache one more file resulted in removing existing ones from the cache. Then playing with the size of the very last component helped nail the exact number of bytes available in the cache – 105 * 15 + 7 = 1582K.
So the total cache is just a little over 1.5 MB (earlier research showed 0.5MB for earlier iPhone OS). Have in mind that this is the total cache shared between all pages, it's not per domain or per tab.
Memory cache
The iPhone has a little bit more cache space in the form of memory cache. The memory cache has no per-component size limit, so your components can be as big as the memory cache – 1941 bytes (don't ask how many attempts it took to nail this number).
There a little catch though, probably due to a bug, and it's that if the component size divides by 4, it won't be cached in memory. That's true for JavaScripts – they will be requested on every page load. CSS files with sizes over 15 that divide by 4 will not be requested in the same tab session, but if you close the tab and open the page in another tab, they will be requested again.
So components under 15K go to the disk cache, a 16K component is never cached (so is 20, 24, …, 36,.. 100,… 1024 and so on), 17K and up (up to almost 2 megs) components are cached in memory.
This memory cache is very unreliable as you can guess, because it gets cleared very often, it's probably useful only in the same user session on your web site.
Some observations
- Closing all tabs, except for blank ones (as when you do "New Page") and then closing Safari is the same as clearing the cache from Settings. So it's a good idea in your normal daily use to leave at least one page open, before you close Safari in order not to cause the implicit cache clearing.
- Tapping the reload icon in the address bar sends unconditional requests for all components, without the If-Modified-Since header and ignoring the Expires header. So to speed up your browsing, refresh the page by tapping the address bar and then tapping GO, don't use the refresh icon.
- The tests I ran were using OS 3.0 and OS 3.1.2. I got my phone last Christmas which means that according to this page it came with OS 2.2. I don't remember if I ran any tests with OS2.2. too. I do remember though that I tried some tests back when I didn't have a phone and asked help from Ryan Grove and Nicole Sullivan. Chatting over IM they were loading pages while I was tail-ing the server log. Those tests must have been with OS 2 or OS2.1. Back then the results showed that the limit for a component that gets stored in the cache was 10Meg, which was also the total size of the cache. Now I have my doubts that back then we only tested memory cache, not disk cache. In any event, it's important to note that those restrictions are all software restrictions. No matter what model the phone, it's the OS that sets the limits. Different models with the same OS will behave the same when it comes to caching sizes.
HTML5 offline application cache
All in all we can safely summarize that the iPhone has no cache to speak of. 15K per component is nothing and the limit of 1.5Megs shared with all other pages will have your cached components kicked out pretty quickly.
So – what's an iPhone performance optimizer to do? Use HTML5 goodies.
One thing to consider is some form of client storage supported by the mobile webkit (either key-value or SQLite), but that will require some of your javascript to handle the caching, expiration and so on.
Another thing to do is use the offline application cache. It's meant to support applications to work offline, but it also ends up being useful to improve caching of online applications as well.
What you need is a simple text file called a manifest. In there you list all the components required by your application. E.g.
CACHE MANIFEST /root/path/to/images/image.jpg or/maybe/relative/paths/too/scripts.js http://example.org/good-old/absolute/path/oojs-home.jpg
It's important to serve this file with content type text/cache-manifest
.
Then in your html
tag just point to that file. Let's say you called the manifest mycache.manifest
(could be dynamic, php, or anything, as long as it's served with the proper content type). So your HTML should start like the following:
<!DOCTYPE html> <html manifest="mycache.manifest">
And this is it. There's also JavaScript API available if you want to play with the items stored in the cache.
Now the browser will request the manifest file every time (Expires header won't help you here) and if its contents is changed it will quietly and unobtrusively download the updated components in the background and the next time the user will see the updated page. Needless to say it makes sense that all the pages of the site share the same manifest.
The best part is that when using offline cache none of the restrictions mentioned above apply – you can store files bigger than 15K and you don't share your total cache space with anyone.
I used this for a personal project – whomsy.com if you want to play with some requests and monitor the traffic (e.g. using the iPhone simulator and Charles proxy).
Manifest – not just for iPhone?
The manifest idea sounds so nice, it makes sense to apply it to desktop browsers as well. In a way it's similar to the idea of having web archives – a zip with all page components (sorry, can't find the URL for the proposal for web archives right now). You can include the manifest on your homepage and, after onload, the browser preloads all the other components used by internal pages (no need to do preloading yourself in JavaScript, no need to worry about spriting images and so on). This is absolutely doable and it also provides the side benefit that your app is available offline, which is the original purpose of the offline cache 🙂 However the application of this technique is a little limited.
- Browser support – currently Webkit browsers support manifests, and IE doesn't. Firefox does support offline cache, however it shows an ugly confirmation message at the top of the page and you normally don't want to stress out your users with warning messages (see screenshot below)
- The "homepage" meaning the page that includes the cache manifest also gets cached implicitly. This could be undesired behavior for many sites, but could work just fine for Ajaxy one-page-sites and applications.
Overall, I'm pretty enthusiastic about using offline cache for the purposes of normal page caching too. The future is here, it's just not evenly distributed (nor supported in IE) yet 🙂