Making a mobile connection
I just returned from Breaking Development Conference, an amazing gathering of many of the brightest minds in mobile web development. On the flight home I watched the video ($$) and slides from Rajiv Vijayakumar’s talk on Understanding Mobile Web Browser Performance at Velocity 2011. Rajiv works at Qualcomm where his team has done extensive performance analysis of the Android browser. Some of their findings include:
- Android 2.2 has a max of only 4 HTTP connections which limits parallel downloads. (This was increased to 8 in Android 2.3 and 35 in Android 3.1 according to Browserscope.)
- It supports pipelining for reduced HTTP overhead.
- Android’s cache eviction is based on expiration date. This is a motivation for setting expiration dates 10+ years in the future.
- Android closes TCP sockets after 6 seconds of inactivity.
This last bullet leads to an interesting discussion about the tradeoffs between power consumption and web performance.
Radio link power consumption
3G devices surfing the Web (do people still say “surfing”?) establish a radio link to the carrier’s cell tower. Establishing and maintaining the radio link consumes battery power. The following graph from Rajiv’s slides shows power consumption for an Android phone while loading a web page. It rises from a baseline of 200 mA to ~400 mA as the radio link is initialized. After the page is loaded the phone drops to 300 mA while the network is inactive. After 10 seconds of inactivity, the radio link reaches an idle state and power consumption returns to the 200 mA baseline level.
The takeaway from this graph is that closing the radio link sooner consumes less battery power. This graph shows that the radio link continues to consume battery power until 10 seconds of inactivity have passed. The 10 second radio link timer begins once the web page has loaded. But there’s also a 6 second countdown after which Android closes the TCP connection by sending a FIN packet. When Android sends the FIN packet the radio link timer resets and continues to consume battery power for another 10 seconds, resulting in a total of 16 seconds of higher battery consumption.
One of the optimizations Rajiv’s team made for the Android browser running on Qualcomm chipsets is to close the TCP connections after the page is done loading. By sending the FIN packet immediately, the radio link is closed after 10 seconds (instead of 16 seconds) resulting in longer battery life. Yay for battery life! But how does this affect the speed of web pages?
Radio link promotion & demotion
The problem with aggressively closing the phone’s radio link is that it takes 1-2 seconds to reconnect to the cell tower. The way the radio link ramps up and then drops back down is shown in the following figure from an AT&T Labs Research paper. When a web page is actively loading, the radio link is at max power consumption and bandwidth. After the radio link is idle for 5 seconds, it drops to a state of half power consumption and significantly lower bandwidth. After another 12 seconds of inactivity it drops to the idle state. From the idle state it takes ~2 seconds to reach full power and bandwidth.
These inactivity timer values (5 seconds & 12 seconds in this example) are sent to the device by the cell tower and thus vary from carrier to carrier. The “state machine” for promoting and demoting the radio link, however, is defined by the Radio Resource Control protocol with the timer values left to the carrier to determine. (The protocol dubs these timer values “T1”, “T2”, and “T3”. I just find that funny.) If the radio link is idle when you request a web page, you have to wait ~2 seconds before that HTTP request can be sent. Clearly, the inactivity timer values chosen by the carrier can have a dramatic impact on mobile web performance.
What’s your carrier’s state machine?
There’s an obvious balance, sort of a yin and yang, between power consumption and web performance for 3G mobile devices. If a carrier’s inactivity timer values are set too short, users have better battery life but are more likely to encounter a ~2 second delay when requesting a web page. If the carrier’s inactivity timer values are set too long, users might have a faster web experience but shorter battery life.
This made me wonder what inactivity timer values popular carriers used. To measure this I created the Mobile State Machine Test Page. It loads a 1 kB image repeatedly with increasing intervals between requests: 2, 4, 6, 11, 13, 16, and 20 seconds. The image’s onload event is used to measure the load time of the image. For each interval the image is requested three times, and the median load time is the one chosen. The flow is as follows:
- choose the next interval
i
(e.g, “2” seconds) - wait
i
seconds - measure
t_start
- request the image
- measure
t_end
using the image’s onload - record
t_end - t_start
as the image load time - repeat steps 2-6 two more times and choose the median as the image load time for interval
i
- goto step 1 until all intervals have been tested
The image should take about the same time to load on every request for a given phone and carrier. Increasing the interval between requests is intended to see if the inactivity timer changes the state of the radio link. By watching for a 1-2 second increase in image load time we can reverse engineer the inactivity timer values for a given carrier.
I tweeted the test URL about 10 days ago. Since then people have run the test 460+ times across 71 carriers. I wrote some code that maps IP addresses to known carrier hostnames so am confident about 26 of the carriers; the others are self-reported. (Max Firtman recommended werwar for better IP-to-carrier mapping.) I’d love to keep gathering data so:
I encourage you to run the test!
The tabular results show that there is a step in image load times as the interval increases. (The load time value shown in the table is the median collected across all tests for that carrier. The number of data points is shown in the rightmost column.) I generated the chart below from a snapshot of the data from Sept 12.
The arrows indicate a stepped increase in image load time that could be associated with the inactivity timer for that carrier. The most pronounced one is for AT&T (blue) and it occurs at the 5 second mark. T-Mobile (yellow) appears to have an inactivity timer around 3 seconds. Vodafone is much larger at 15 seconds. Sprint and AT&T Verizon have similar profiles but the step is less pronounced.
There are many caveats about this study:
- This is a small sample size.
- The inactivity timer could be affected by other apps on the phone doing network activity in the background. I asked people to close all apps, but there’s no way to verify they did that.
- A given carrier might have different kinds of networks (3G, 4G, etc.). Similarly, they might have different inactivity timer values in different regions. All of those different conditions would be lumped together under the single carrier name.
What’s the point?
Hats off to Rajiv’s team at Qualcomm for digging into Android browser performance. They don’t even own the browser but have invested heavily in improving the browser user experience. In addition to closing TCP connections once the page is loaded, they increased the maximum number of HTTP connections, improved browser caching, and more.
I want to encourage this holistic approach to mobile performance and will write about that in more depth soon. This post is pretty technical, but it’s important that mobile web developers have greater insight into the parts of the mobile experience that go beyond HTML and JavaScript – namely the device, carrier network, and mobile browser.
For example, in light of this information about inactivity timers, mobile web developers might choose to do a 1 pixel image request at a set interval that keeps the radio link at full bandwidth. This would shorten battery life, so an optimization would be to only do a few pings after which it’s assumed the user is no longer surfing. Another downside is that doing this would use more dedicated channels at the cell tower, worsening everyone’s experience.
The right answer is to determine what the tradeoffs are. What is the optimal value for these inactivity timers? Is there a sweet spot that improves web performance with little or no impact on battery life? How did the carriers determine the current inactivity timer values? Was it based on improving the user’s web experience? I would bet not, but am hopeful that a more holistic view to mobile performance is coming soon.
Waterfall UI Conventions
I spend a lot of time looking at HTTP waterfall charts. Each browser has its own waterfall chart tool which makes sense since the way to track network events differs across browsers. There are some tools that span multiple browsers, like HttpWatch. Since I work across all browsers I’m impacted by the lack of UI consistency across these tools. This blog post summarizes the current state and proposes the adoption of waterfall UI conventions.
It’s easy to see the inconsistencies looking at these waterfall charts from the tools I use most frequently.* Each screenshot shows the waterfall for Wikipedia. The main areas I’m going to discuss are the colors and names for connection states, Content-Types, and loading events. There are many other areas where consistency would be nice – such as overall layout and default columns – but those are more subjective and the tool owner might feel their choices make their tool more preferred. The consistency changes I’m suggesting don’t effect the information shown, just how it looks.
One big difference is the information shown in the horizontal bar. Chrome Developer Tools uses the horizontal bar to reflect the Content-Type: blue for HTML, green for CSS, purple for images, etc. All the other tools use the bar to show the length of each connection state: DNS lookup, sending the request, downloading the response, etc. I find the Content-Type more useful, but rather than debate one over the other I most like HttpWatch’s approach where they show both: the connection states in the bar and the Content-Type in a tiny icon (see the “Type” column). Even if the other tools didn’t want to show icons, they could use font colors to reflect Content-Type. Let’s explore the connection states and Content-Type choices across the tools.
Connection States
The names and colors used for the different network connection states vary across the tools. In some cases, the granularity of connection states varies as well. The colors and names used by each tool are shown in this table:
Chrome Dev Tools | Blocking | DNS Lookup | Connecting | Sending | Waiting | Receiving |
Firebug | Blocking | DNS Lookup | Connecting | Sending | Waiting | Receiving |
HttpWatch | Blocked | DNS Lookup | Connect | Send | Wait | Receive |
IE9 Dev Tools | Wait | Start | Request | Response | ||
WebPagetest | DNS Lookup | Initial Connection | Time to First Byte | Content Download |
Let’s look at the names first. Chrome Dev Tools and Firebug use the same names for every connection state: Blocking, DNS Lookup, Connecting, Sending, Waiting, and Receiving. All of these names are progressive verb forms except “DNS Lookup” – saying “looking up DNS” would be painful. I’d prefer simple verb forms which would also give us consistent tense across all names: Block, Lookup DNS, Connect, Send, Wait, and Receive. It’s also important to get similar connection states across all tools: IE9 Dev Tools and WebPagetest don’t show blocking and combine send & wait into a single state.
The colors are much more inconsistent. Chrome Dev Tools uses the same color for all states. The rest of the tools have almost no overlap. Here’s my proposal:
- Block (gray) – because nothing is really happening
- Lookup DNS (yellow) – like the Yellow Pages
- Connect (red) – because this is the tricky negotiation part (red is caution)
- Send (blue) – it’s a good color that contrasts well with red
- Wait (light purple) – a mellow color while we wait
- Receive (green) – because this is the payment that’s been received (green like money – sorry for the U.S. bias)
These are subjective choices and I’m open to other proposals. I most care about gray for Block and yellow for Lookup DNS. I also defer to someone who understands the color wheel. (I painted for years but never learned.)
Content-Type
Chrome Dev Tools is the only tool that reflects the Content-Type in the waterfall’s horizontal bars. The choice of whether to use the horizontal bar to show Content-Type or connection states is up to the tool developer. My preference would be to follow Chrome Dev Tools and use the bar to show Content-Type. A pop-up or detail view could be used to see the connection state information. Chrome Dev Tools, Firebug, HttpWatch, and IE9 Dev Tools already display a detailed view of connection states when you select a bar.
Regardless of the information shown in the horizontal bars, users would benefit in other ways from consistent colors mapped to Content-Type. This color mapping could be used as the text color in the waterfall chart and in charts of total requests and download size broken out by Content-Type.
The color map from Chrome Dev Tools is:
- HTML (blue)
- JavaScript (orange)
- CSS (green)
- images (purple)
- text/plain (yellow)
- redirect (gray)
I’m fine with these colors. If it was up to me I’d make JavaScript red because I have so many errors in my JavaScript. I’d make CSS purple because that’s “fancier” (CSS is used to make pages look more stylish). I’d make images blue because they’re the most common resource type and my world is mostly blue (it’s a denim thing, not emotions). That leaves green for HTML. But again, purely subjective.
Load Events
Many of the tools draw a vertical line to mark the DOMContentLoaded and window load events. Again, the names and colors vary across the tools:
Chrome dev tools | DOMContent | Load |
Firebug | DOMContentLoaded | load |
HttpWatch | Render Start | Page Load |
IE9 dev tools | DOMContentLoaded | Load |
WebPagetest | Start Render | Document Complete |
I like DOMContentLoaded and Load because I understand exactly what’s being measured. I’m less concerned about the colors; I’d pick blue and green if it was up to me.
Now what?
I’m working with Brian Pane and Jan Odvarko on some UI changes to Jan’s (Honza’s) HAR Viewer. I hope we’ll add Content-Type icons, in which case other tools could adopt those. If you’d be willing to create those icons please contact me.
As for the names and colors, I’m not sure how to proceed. Mark Nottingham suggested starting an “ether pad or wiki page”. I’d appreciate comments on these ideas and ways to move forward. Greater consistency across these tools will make it easier for developers to get on the web performance bandwagon, which is something I hope we all want.
HTTP Archive: nine months
Although the HTTP Archive was announced in March, I actually started gathering data back in November of 2010. This week’s run marks nine months from that initial crawl. The trends show that performance indicators are mixed, with some critical metrics like size and redirects on the rise.
[As a reminder, the HTTP Archive currently crawls approximately 17,000 of the world’s top websites. All of the comparisons shown here are based on choosing the “intersection” of sites across all of those runs. There are ~13K sites in the intersection.]
The transfer size of pages has increased 15% (95 kB) over nine months. The average size is now up to 735 kB. Note that this is the transfer size. Many text resources (including HTML documents, scripts, and stylesheets) are compressed so the actual size is larger. The bulk of this growth has been in images – up 18% (66 kB). Scripts have had the greatest percentage increase growing 23% (25 kB).
Note that these sizes are the total size of all images in the page and all scripts in the page, respectively. The average size of individual resources has stayed about the same over this nine month period. If individual resource size is the same, how is it that the total page size has increased? The increase in total transfer size is the result of a 10% increase in HTTP requests per page – that’s seven more resources per page.
Redirects are known to cause page delays, and yet the percentage of sites containing at least one redirect increased from 58% to 64%. Requests that fail are wasteful using connections that could have been used more productively, but sites with errors grew from 14% to 25%.
All the news isn’t gloomy. The use of Google Libraries API has increased from 10% to 14%. This is good for performance because it increases the likelihood that as a user navigates across sites the most common resources will be in their cache. In addition, serving those from the Google Libraries servers might be faster and more geographically distributed for smaller sites.
The use of Flash has dropped 2% from 47% to 45% of websites. Flash resources average 58 kB which is much larger than other resources, and there are fewer tools and best practices for optimizing Flash performance.
There are still many resources that do not have the necessary HTTP response headers to make them cacheable. Luckily the trend is moving toward more caching: the 61% of resources that did not have headers to make them cacheable has dropped to 58%. Stating the inverse, the number of resources with caching headers grew from 39% to 42% (+3%).
Here’s a recap of the performance indicators from Nov 15 2010 to Aug 15 2011 for the top ~13K websites:
- total transfer size grew from 640 kB to 735 kB
- requests per page increased from 69 to 76
- sites with redirects went up from 58% to 64%
- sites with errors is up from 14% to 25%
- the use of Google Libraries API increased from 10% to 14%
- Flash usage dropped from 47% to 45%
- resources that are cached grew from 39% to 42%
My kids started school this week. I’m hoping their first report card looks a lot better than this one.
(lack of) Caching for iPhone Home Screen Apps
Yesterday’s post, Unexpected Reloads in WebKit, revealed an interesting behavior that affects caching in Safari:
When you load the same URL back-to-back in Safari, the second load is treated the same as hitting Reload.
This is bad for performance because the browser issues a Conditional GET request for each resource instead of using the cached resource.
It’s important to be aware of this behavior when testing the primed cache experience in Safari, so web performance engineers should take note. However, in the real world it’s unlikely this behavior has much of an impact on desktop users. Here’s the table from yesterday’s post that shows how this Reload-like behavior is triggered when re-requesting a page:
way of loading URL again | like Reload? |
---|---|
hit RETURN in location field | yes |
delete URL and type it again | yes |
launch same URL via bookmark | yes |
click link to same URL | yes |
go to another URL then type 1st URL again | no |
modify querystring | no |
enter URL in a new tab | no |
Table 1. Triggering reload behavior in Safari |
It’s possible that real world users might type the same URL or open the same bookmark two times in a row in the same tab, but it probably doesn’t happen that often. So what’s the big deal?
So what’s the big deal?
Whenever I see strange performance behavior I think about where that behavior might have a significant impact. Is there any place where this back-to-back Safari Reload behavior could have a significant impact? A comment from yesterday’s post hints at the answer:
Why is this article named “Unexpected Reloads in WebKit�
Chrome is based on Webkit and doesn’t has same issue. Perhaps it would be less confusing to name it “Unexpected Reloads in Safariâ€.
Other people gave me the same feedback on the backchannel – why did I say “WebKit” instead of “Safari”.
Here’s why: WebKit is used in a lot of browsers. Whenever I see a bug (or a feature) in one popular WebKit-based browser I wonder if it exists in others. The main WebKit-based browsers I focus on are Chrome, Safari, Android, and iPhone. As soon as I noticed this behavior in Safari my next step was to conduct the same tests in Chrome, Android, and iPhone. As the commenter noted, this unexpected Reload behavior does not happen in Chrome. And it does not happen on Android (tested on my Nexus S). But it does happen on iPhone.
While it’s true that iPhone users are unlikely to manually launch the same URL twice-in-a-row in the same tab, there is a situation when this happens automatically: when launching home screen apps.
Home screen apps are a powerful feature on iPhone and Android that allow users to save URLs to the home screen and launch them similar to native apps. Unfortunately, launching home screen apps on the iPhone triggers something similar to the Reload behavior we see in Safari – where resources aren’t read from cache and instead generate extra HTTP requests. Let’s take a look at a few examples of home screen apps, starting with simple to more complex.
Amazon: simple URL
Typing http://www.amazon.com/ into the iPhone browser displays a version of Amazon’s front page that is customized for mobile – there’s less content, the images are smaller, etc. However, there is not a prompt to save the URL to the home screen. We can do that anyway using the arrow function key at the bottom of the screen and selecting “Add to Home Screen”.
If you’ve used home screen apps you might have noticed that they always open in the same browser tab. Let’s run a little test to confirm this:
- Click the Amazon home screen icon. This opens Amazon in mobile Safari.
- Open another tab by clicking the “pages” function key and opening a “New Page”. Enter some non-Amazon URL in this new tab, for example http://fast.stevesouders.com/ (a very lightweight page I use for testing). At this point we have at least two tabs, one with Amazon and one with fast.stevesouders.com, and we’re looking at the fast.stevesouders.com tab.
- Go back to the home screen and click the Amazon icon again.
- Note that you’re taken back into mobile Safari to the first tab that contains Amazon.
We just opened the exact same URL back-to-back in the same tab. We didn’t do it intentionally – that’s the default behavior for iPhone home screen apps. Here’s a waterfall chart for this test. (You can view an interactive waterfall by loading the HAR file in pcapperf.)
The home screen app URL is http://www.amazon.com/gp/aw/h.html/187-9233150-9797455. The first time the home screen app is launched starts at the top with 187-9233150-9797455. Since the cache was empty all the subsequent resources have 200 responses. There are some 404s for icons followed by the request for fast.stevesouders.com.
The second launch of the Amazon home screen app (187-9233150-9797455 below fast.stevesouders.com) is where it gets interesting. When the Amazon home screen app is launched the second time, a Conditional GET request is made for all of the resources even though these resources are in the cache with a future expiration date.
All of the resources that are re-requested have an expiration date more than 10 years in the future. For example, the response headers for title_gradient._V233984477_.png are:
content-length: 291 expires: Tue, 06 May 2031 21:44:21 GMT last-modified: Mon, 10 Aug 2009 11:50:45 GMT cache-control: max-age=626560472 date: Wed, 29 Jun 2011 01:09:49 GMT content-type: image/png
We know it was cached because when the Amazon home screen app is launched the second time the Conditional GET request for title_gradient._V233984477_.png has an If-Modified-Since header that contains the last-modified date in the initial response:
if-modified-since: Mon, 10 Aug 2009 11:50:45 GMT
It appears that we’ve stumbled into the Reload-like behavior we saw in Safari on the desktop. Further evidence of this is if you launch the home screen app, then type a new URL over the Amazon URL, and launch the home screen app again the resources are read from cache instead of generating numerous Conditional GET requests. (Load this HAR file in pcapperf to see for yourself.)
Untappd: full screen app
Amazon was a simple home screen app – really just a bookmark on the home screen. Developers can do more with home screen apps to make them launch and look like native apps. As described in Apple’s How-To’s for Safari on iPhone, various parts of the home screen app user experience are customizable including the home screen icon, viewport, and zooming and scaling. Developers can also have their home screen app launch in “full screen mode” by hiding the Safari UI components, including the status bar and location bar. In this situation, every time the home screen app is launched it uses the same “tab” with the exact same URL – thus triggering the Reload behavior.
Let’s have a look at Untappd on the iPhone. The first time you navigate to http://untappd.com/ in iPhone’s browser you get a suggestion to add the web app to the home screen:

After which you’ll have a customized Untappd home screen icon:

Now let’s investigate how caching works for this home screen app. We start by clearing the cache then launching the home screen app. You’ll notice there is no location bar or other Safari controls. Then we go back to the home screen and launch the Untappd home screen app again. The waterfall chart is shown below. (Here’s the HAR file.)
The first time the Untappd home screen app is launched it loads seven HTTP requests. Three of these resources are cacheable: jquery.min.js (1 year), gears_init.js (1 hour), and ga.js (1 day). Loader.gif and ajax-loader.png don’t have a future expiration date, but they do have Last-Modified and ETag response headers that could be used in a Conditional GET request.
But we see that the second time Untappd is launched from the home screen, all of the resources are re-requested. To make matters worse, none of these are Conditional GET requests, so a 200 status code is returned with the full response body.
The punchline
It’s unfortunate that home screen apps suffer from this bad caching behavior on the iPhone. Thankfully, there is a workaround: application cache. I ran similar tests on other home screen apps that use application cache. The resources listed in the CACHE: section of the manifest file were used on the iPhone without generating Conditional GET requests.
I feel bad about recommending the use of application cache. This is an issue with the browser cache on mobile Safari (and to a lesser degree on desktop Safari) that should be fixed. It’s a significant amount of work for developers to adopt application cache. The plus side is that doing so achieves the ability to work offline.
After this lengthy analysis and numerous waterfalls, here’s the punchline in a nutshell:
Home screen apps on iPhone are slower because resources are re-requested even though they should be read from cache. Use application cache to avoid this performance problem.
Unexpected Reloads in WebKit
People who work on web performance often need to load the same URL over and over again. Furthermore, they need to do this while simulating a real user’s empty cache experience and primed cache experience. When I want to analyze the empty cache experience the flow is simple: go to about:blank, clear the browser cache, enter the URL, and hit RETURN.
But what’s the right way to fetch a page repeatedly when analyzing the primed cache experience?
The main goal when testing the primed cache version of a page is to see which resources are read from cache. The goal for better performance is to cache as many responses as possible thus reducing the number of requests made when the cache is primed. If a resource has an expiration date in the future, the browser uses the cached version and doesn’t have to make an HTTP request resulting in a faster page. If a resource is expired (the expiration date is in the past) the browser issues a Conditional GET request using the If-Modified-Since and If-None-Match request headers. If the resource hasn’t changed then the server returns a simple 304 status code with no body. This is faster (because there’s no response body) but still takes time to do the HTTP request. (See my article on ETags for examples of IMS and INM.)
One way to re-request a page is to hit the Reload button, but this doesn’t give an accurate portrayal of the typical primed cache user experience. Hitting Reload causes the browser to always make an IMS/INM request for resources in the page, even for cached resources that have an expiration date in the future. Normally these resources would be used without generating an HTTP request. Although users do occasionally hit the Reload button it’s more likely that they’ll navigate to a page via a link or the location field, both of which avoid the time consuming Conditional GET requests generated when hitting Reload.
The technique I adopted years ago for re-requesting a page when testing the primed cache is to click in the location field and hit RETURN. That’s a fine approach in IE, Firefox, Chrome, and Opera, but not in Safari. Let’s investigate why.
hitting RETURN in the location field
I’m using Untappd as an example. Untappd has 68 requests when loaded on the desktop. Figure 1 shows the waterfall chart for the first 31 requests when loaded in Firefox 4 with an empty cache:
- Figure 1. untappd.com – Firefox 4 – empty cache
Most of the resources shown in Figure 1 have an expiration date in the future and therefore won’t generate an HTTP request if the user has a primed cache. To test that I click in the location field and hit RETURN. The resulting waterfall chart is shown in Figure 2. Sure enough the number of HTTP requests drops from 68 to 4!
- Figure 2. untappd.com – Firefox 4 – primed cache
If you repeat this experiment in Chrome, Firefox, Internet Explorer, and Opera you’ll get similar results – empty cache generates 68 requests, primed cache generates 4 requests. However, the result is very different in Safari 5. It’s important to understand why.
Safari is different
This test shows that Untappd has done a good job of optimizing the primed cache experience – the number of HTTP requests made by the browser drops from 68 to 4. Running the same test in Safari 5 produces different results. Clearing the cache and loading untappd.com in Safari 5 loads 68 HTTP requests – just as before. To test the primed cache experience we click in the location field and hit RETURN. Instead of only 4 requests there are 68 HTTP requests.
Why are there 64 more HTTP requests in Safari 5 for the primed cache test? Looking at the HTTP request headers we see that these are all Conditional GET requests. Let’s use http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js as the example (it’s the 8th request in Figure 1). In the empty cache scenario the HTTP request headers are:
Accept: */* Cache-Control: max-age=0 Referer: http://untappd.com/ User-Agent: Mozilla/5.0 (Macintosh; [snip...] Safari/533.20.27
The HTTP status code returned for that empty cache request is 200 OK.
In the primed cache test when we hit RETURN in the location field we see that the request for jquery.min.js contains an extra header:
Accept: */* Cache-Control: max-age=0 If-Modified-Since: Mon, 15 Feb 2010 23:30:12 GMT Referer: http://untappd.com/ User-Agent: Mozilla/5.0 (Macintosh; [snip...] Safari/533.20.27
The header that’s added in the primed cache test is If-Modified-Since. This is a Conditional GET request. The HTTP status code that’s returned is 304 Not Modified. Even though all I did was hit RETURN in the location field, Safari treated that like hitting the Reload button.
unexpected “reload” in Webkit
Unlike other browsers, Safari 5 treats hitting RETURN in the location field the same as clicking the Reload button. When else does this happen? Assuming you’ve loaded a URL in Safari and are looking at that page, this table lists various ways to load that URL again. For each technique I show whether loading the URL this way generates extra Conditional GET requests similar to clicking Reload.
way of loading URL again | like Reload? |
---|---|
hit RETURN in location field | yes |
delete URL and type it again | yes |
launch same URL via bookmark | yes |
click link to same URL | yes |
go to another URL then type 1st URL again | no |
modify querystring | no |
enter URL in a new tab | no |
Table 1. Triggering reload behavior in Safari |
This black box testing indicates that whenever the same URL is loaded back-to-back in the same tab, Safari 5 treats it as a Reload. I was describing this behavior to Jay Freeman (saurik) at Foo Camp. He pointed me to this code from WebCore:
else if (sameURL) // Example of this case are sites that reload the same URL with a different cookie // driving the generated content, or a master frame with links that drive a target // frame, where the user has clicked on the same link repeatedly. m_loadType = FrameLoadTypeSame;
Searching in that same file for FrameLoadTypeSame we find this code:
case FrameLoadTypeReload: case FrameLoadTypeReloadFromOrigin: case FrameLoadTypeSame: case FrameLoadTypeReplace: history()->updateForReload(); m_client->transitionToCommittedForNewPage(); break;
This code doesn’t account for the behavior, but it does show that FrameLoadTypeSame and FrameLoadTypeReload are treated as similar cases in this context, and perhaps that’s why IMS/INM requests are generated.
One important takeaway from this is: don’t hit RETURN in the location field to test primed cache experience in Safari. Instead, go to a different URL and then type the test URL in the location field, or open a new tab and type the URL.
There’s a second more important takeaway from this. I’ll cover that in tomorrow’s post. If you know the answer, please don’t spoil it. Oh what the heck – if you think you know the answer go ahead and add a comment.
HTTP Archive: 1M URLs, Internet Archive, Sponsors
The HTTP Archive provides a permanent record of web performance information. It started in October 2010 crawling 1K URLs. This was possible thanks to Pat Meenan’s help providing access to WebPagetest. A month later we increased coverage to the world’s top ~18K URLs. That was good, but the next step is 1M URLs. Today at Velocity I made two announcements that pave the way for achieving this goal.
Starting today the HTTP Archive is part of the Internet Archive. I met Brewster Kahle several years ago and have always admired the work the Internet Archive has done building a “digital library of Internet sites.” When I approached him about this merger we both saw it as an obvious fit. In addition to preserving a record of the content of these sites (via the Wayback Machine) we agreed it’s important to record how that content is built and served. It makes sense that researchers, historians, and scholars be able to find both sets of information under one roof. I’ll continue to run the HTTP Archive project.
The following companies have agreed to sponsor the work of the HTTP Archive: Google, Mozilla, New Relic, O’Reilly Media, Etsy, Strangeloop, and dynaTrace Software. In order to grow to 1M URLs we need data center space, servers, licenses, etc. Thanks to these sponsors we’ve started to build out this infrastructure and will be increasing our coverage soon.
I look forward to working with the Internet Archive on our mission of preserving a record of the Web for generations to come. If you would like to join the effort, I invite you to make a donation to the Internet Archive and contribute your coding skills to the open source project.
HTTP Archive: today’s runs
I just finished processing the “May 16 2011” run for HTTP Archive and HTTP Archive Mobile. Here are some interesting observations.
HTTP Archive (desktop)
As of today there is six months of historical data in the HTTP Archive. As a reminder, the world’s top ~17K web pages are being crawled. Since the actual URLs in the list at any given time can change, I start by looking at the trends for the intersection of URLs. This means the list of URLs is exactly the same for each data point. The total transfer size continues to grow: up 8 kB (1.1%) since the last run on Apr 30 2011, and up 53 kB (8%) from the first run back on Nov 16 2010.
Looking at the Image Transfer Size chart we see images are the main cause for this growth – up 5 kB from Apr 30 2011 and 43 kB since Nov 16 2010. Conversely, the transfer size of Flash has steadily dropped from 81 kB on Nov 16 2010 down to 71 kB on this last run on May 16 2011 – a 12% decrease.
I used the new Compare Stats page to compare the interesting stats from Nov 15 2010 to May 16 2011. I again chose the “intersection” of URLs to get an apples-to-apples comparison. In addition to the transfer size increases mentioned previously, we can see the growth of various JavaScript libraries on these ~17K web pages over the last six months:
- jQuery is up from 39% to 44%
- Facebook widgets have grown from 8% to 13%
- Twitter widgets increased from 2% to 4%
The “Pages Using Google Libraries API” chart shows adoption of this CDN has grown from 10% to 13% since Nov 16 2010. This means even more cross-site caching benefits for users.
HTTP Archive Mobile
The HTTP Archive Mobile just launched last week. There’s only two weeks of historical data gathered on just the top 100 web pages, so there aren’t any major shifts in the trending charts just yet. Nevertheless, comparing the mobile stats between the last two runs, May 12 and May 16, has some interesting revelations.
The “Pages with the Most JavaScript” chart shows that the Twitpic page’s JavaScript grew from 308 kB to 374 kB – a 66 kB (21%) increase. This highlights the value of HTTP Archive (both desktop and mobile) to individual websites as a way to track performance stats and have a permanent history.
There are some other interesting stats at the individual website level, but we’ll need a few months of mobile data before we can draw conclusions about any trends. In the meantime, check back here around the 15th and 30th of each month to see the latest runs and discover new observations about how the web is running.
HTTP Archive Mobile
This morning during my talk at Mobilism I announced the HTTP Archive Mobile – a permanent repository for mobile performance data.
Ever since I announced the HTTP Archive (based on data gathered from Internet Explorer using WebPagetest) people have been asking, “What about data gathered from a mobile device?” It’s a logical next step. Thankfully, Blaze.io came on the scene a few months ago with a solution, Mobitest, for connecting to mobile devices and gathering this kind of information. Blaze.io has been doing great work in the area of mobile performance. I met Guy Podjarny, Blaze.io’s CTO, a few months ago and we’ve had several discussions about performance. A few weeks ago Guypo and I decided to start working on the HTTP Archive Mobile. It required a few changes on his side and a few on mine, but we quickly got it up and running and have been gathering data for the past week in anticipation of this announcement.
Here are some interesting comparisons of the Top 100 web pages from desktop vs mobile:
- Total bytes downloaded is 401 kB for desktop vs 271 kB for mobile.
- Total size of images, scripts, and stylesheets is smaller on mobile, but size of HTML is bigger. This is likely from JavaScript, CSS, and images being inlined in the HTML document – a good sign for performance.
- The New York Times is in the top 5 pages with the most JavaScript with 230 kB on desktop and 326 kB on mobile – 94 kB more JavaScript on mobile than desktop.
- The percentage of sites that have at least one redirect is 54% for desktop compared to 68% for mobile. Many websites (Facebook, Yahoo!, Bing, Taobao, etc.) redirect from www.* to m.* on mobile browsers. But it would be better to lower the mobile percentage since redirects inflict an even greater delay on mobile devices.
This is just the beginning. The HTTP Archive Mobile is currently in “Alpha” mainly because we’re gathering data for just the Alexa Global Top 100 sites and only have a week’s worth of data. More URLs and more data are on the way. Already there are valuable takeaways from what’s there, and these will increase as the number of websites and length of time grow. Take a look for yourself and add comments below on anything you find that’s interesting or puzzling. And thanks again to Guypo and Blaze.io for making the Mobitest framework available for collecting the data.
HTTP Archive: Top 100, Top 1000 & compare stats
Right now the HTTP Archive analyzes the world’s top 17,000 web pages gathering information about the site’s construction. It’s interesting data, especially for a performance junkie like me. Subsetting the data for comparisons is a challenge given the numerous ways this long list of URLs could be sliced.
This past week I added two new subsets: Top 100 and Top 1000. So now when you go to the Trends and Stats pages you have the following choices:
- All – The entire set of ~17K web pages but the exact URLs might vary from run to run due to errors, etc.
- intersection – The set of URLs for which data was gathered in every run. Right now there are ~15K that have data in every run so far. This is great for apples-to-apples comparisons.
- Top 100 – The world’s Top 100 based on Alexa.
- Top 1000 – The world’s Top 1000 based on Alexa.
There are some amazing differences between these sets of sites. To make it easier to explore these differences I added the Compare Stats page. You can pick two different runs and sets of URLs and see their stats charts side-by-side. I’m amazed at some of the differences between the Top 100 and Top 1000 for stats from the April 30 2011 run:
- total bytes downloaded: 401 kB vs 674 kB (Top 100 vs Top 1000)
- use of jQuery: 27% vs 43%
- use of Google Analytics: 25% vs 52%
- pages using Flash: 35% vs 49%
- resources with no caching headers: 26% vs 41%

Top 100 Bytes Downloaded

Top 1000 Bytes Downloaded
One takeaway is that stats that are critical for good performance (bytes downloaded, caching) worsen when we expand from the Top 100 to the Top 1000. What happens if we look further down the tail at “All”? I encourage you to check it out yourself and see how your site compares.
HTTP Archive: servers and most 404s
I launched the HTTP Archive about a month ago. The reaction has been positive including supportive tweets from Tim O’Reilly, Werner Vogels, Robert Scoble, and John Resig. I’m also excited about the number of people that have already started contributing to the project. Two new stats charts are available thanks to patches from open source contributors.
James Byers contributed the patch for generating the Most Common Servers pie chart. This chart is similar to BuiltWith’s Web Server chart. BuiltWith shows a higher presence of IIS than shown here. Keep in mind the sample sets are different – the HTTP Archive hits the world’s top ~17K URLs while BuiltWith is covering 1M URLs.
The other new chart comes from Carson McDonald. It shows pages with the most 404s. Definitely a list you don’t want to find your website on.
l’ve added some other features I’ll blog about tomorrow and am planning a bigger announcement later this week, so stay tuned for some more HTTP Archive updates.