A common best practice when building high performance web sites is to serve gzip compressed versions of content such as JavaScript or CSS. Combining gzip compression with a content delivery network (CDN) can dramatically increase the performance of your web site by serving smaller files to users from a location that is closer to them, thus reducing response time and making your sites load faster.
Until now you have been able to support the CDN part of this best practice by using Amazon Web Services CloudFront. However, unlike some other CDNs CloudFront did not support serving gzipped content in a standard manner by using the HTTP Accept-Encoding header that is sent by browsers to indicate their support for gzip compression. There were some workarounds for this issue but they were far from optimal.
With the recent announcement of Custom Origin support in CloudFront it is now possible to use the standard HTTP Accept-Encoding method for serving gzipped content if you are using a Custom Origin. Although not specifically mentioned in the release announcement you can verify this in the Custom Origins Best Practices section of the CloudFront Developer Guide. CloudFront will now forward the Accept-Encoding HTTP header to your origin server where you can ensure the appropriate content is served based on the supported encodings. CloudFront will then cache multiple versions of this content, the uncompressed version and the gzipped version and serve these to clients depending on the value of their Accept-Encoding header for all future requests.
To support this feature on your origin server it should be configured to compress content of the appropriate type when the Accept-Encoding header is present.
For Apache 2.x this means ensuring mod_deflate is loaded and the appropriate content types are sent to the DEFLATE filter with the AddOutputFilterByType directive.
For nginx you need a configuration similar to the following:
gzip on;
gzip_http_version 1.0;
gzip_proxied any;
gzip_types text/css application/x-javascript;
gzip_vary on;
It’s important to note that CloudFront will only make HTTP/1.0 requests to an origin server, thus the requirement for gzip_http_version 1.0 in the nginx configuration. This is also noted in the Custom Origins Best Practices.
A previous post discussed image pre-loading and its impact on user perceived performance. In this post we’ll highlight a technique used by Facebook and Remember The Milk which implements search functionality entirely on the client (browser) side.
Traditionally we’re used to search as a server side function. You visit Google, enter your query and Google’s hundreds of thousands of machines collaborate to produce the most relevant results from the web. Searching billions of documents within 200 milliseconds is pretty impressive by any standard but despite Google’s exemplary efforts there’s a limit to how fast a server-side search can be given that a network connection is still involved.
But what if the dataset being searched is relatively small? Is there a need to run all searches on the server or could it be done in the user’s browser?
Facebook implements client side search in at least two areas: the global site search in the top right hand corner, and the Inbox search. Both of these search fields use a pre-loading technique which loads the user’s entire friend list in a single HTTP request when the user clicks into the search field. This saves Facebook from sending out unnecessary bytes if a user never performs any searches, yet still minimises the user’s wait time as the required data would most likely have finished loading behind the scenes whilst they are typing their query.
The data sizes involved are surprisingly small too. With 226 friends the raw Javascript encoded version of my friend list is about 39kB but with gzip compression it arrives over the network as 10kB. Additionally Facebook sets an Expires: header advising that my friend list can be stored for 1 week so unless my browser fills its cache I should be fetching this 10kB no more than once a week.
Remember The Milk goes a step further by including the user’s entire task list and associated metadata within the initial HTML load of the user’s home page.
The search box is then implemented as:
<form onsubmit="control.updateListFilter(); return false;" action="">
The result is that searches are near instantaneous as they execute entirely within the browser. This is a huge boost to usability, especially for a time/task-management application.
The most obvious question then is how large a user’s task list might get, and the resultant impact on the initial load times of the user’s home page. If you consider a user with 1,000 tasks, and each task is roughly 500 bytes of raw JSON, then the initial page load is at least 500kB heavier but with gzip compression this would reduce to around 100kB of added weight.
However with 10,000 tasks the compressed data size approaches the megabyte mark. Whether this technique still improves the user experience really depends on the user’s usage habits, though I suspect that if a user has 10,000 tasks in RTM they would probably be quite happy about trading off a longer initial load time for the ability to very quickly filter their monster task list. :-)
Finally, with the entire task list loaded in browser there is the issue of updates – what happens when a user adds, modifies, or deletes a task?
A POST is performed to ensure the server state reflects the user’s changes, and Javascript is returned which updates the in-browser state. The following is a Firebug Net view of an “Add Task” operation:

I’ve been a regular Flickr user since the beginning of 2006 and remain a loyal Flickr Pro member despite increasing usage of Facebook’s photos amongst my friends. Putting aside feature & design differences one noticeable difference between the two is in sheer speed of browsing. I decided to compare Flickr, SmugMug and Facebook Photos as they illustrate 3 different approaches to photo browsing, with quite dramatic speed differences.
Flickr takes a whole page approach to photos in that browsing from one photo in a Photostream, Set or Pool loads a whole new HTML page and its associated contents.
SmugMug takes an AJAX approach. Clicking a photo in the current album does not load a new HTML page but triggers two important HTTP requests:

The first is obviously the image requested by the user, the second is any associated comments. Flickr in its whole page load approach can avoid the separate comment load request since it can directly embed comments into the page HTML, however that is still slower than the SmugMug AJAX approach because the page HTML has to be loaded before the browser knows what image to fetch. SmugMug’s AJAX approach enables it to avoid the HTML page load, and also to fetch the image and comments in parallel HTTP requests resulting in a snappier user experience.
Facebook also takes an AJAX approach but goes one step further by preloading images. Upon viewing the first image in an album a simple ((new Image()).src=... is used to preload the next image. Simultaneously, an asynchronous request is made for the album’s metadata which is returned as a JavaScript blob. The metadata describes all images on the current page (of 20) which allows Facebook’s javascript TaskQueue to begin preloading further ahead (at time of writing it preloads up to 4 images ahead of that currently being viewed).

The metadata also contains all comments & tags for every photo on the current page of the album. The current page size is 20 so Facebook has potentially saved 19 HTTP requests by retrieving all comments & tags in one request if the user browses through every photo. The tradeoff is freshness – if a new comment or tag is added during the current browsing session they will not show up, though this would not seem to be a major issue given that in my experience the comment rate is very low on most photos.
The end result is that when advancing from one image to the next the user waits for a single HTTP request for the image in the worst case, and in most cases does not have to wait at all since the images are typically preloaded.
In my testing Flickr’s HTML page alone takes roughly 1.5 seconds to load (450ms waiting for the server to respond, and upwards of 1000ms to load 17kB of HTML). The exact times aren’t as important here as the fact that, ceteris paribus, advancing from one image to the next on Flickr is at the very least 1.5 seconds slower than SmugMug or Facebook.
The pre-loading approach makes sense in some instances. If the user is looking at an image within an album of photos from a birthday party they recently attended they’re quite likely to browse through at least a few other images in the same album. The tradeoff is that inevitably there will be some waste in preloaded bytes. If you’ve clicked into an album because one of your friends was photographed at a conference you probably won’t be clicking through the other 500 photos of random strangers at the conference.
We hope to continue highlighting real world examples of front-end techniques for improving web application performance. If you know of other interesting examples please let us know by leaving a comment below.
Hi there, and welcome to the nomitor blog. We are a small, down to earth company based in Sydney, Australia currently in the development phase of a service that will enable you to monitor and improve the speed of your web sites as experienced by your users.
Our goals for this blog are wide reaching but focused. We will be covering topics related to web performance and availability and how you can apply this information to make your sites measurably faster and more reliable. We will also keep you updated with the latest nomitor developments.
We aren’t ready to launch yet, but if you are interested in improving the speed of your web sites and would like to help us test our service in the near future or would just like to know when we launch you can sign up for notifications on the nomitor home page.
If you want to stay up to date with this blog you can subscribe via RSS or follow our updates on Twitter.