Optimizing HTTP headers for ‘ajax’ webapps

A technical post for a change :)

As you might have guessed, Mibbit relies heavily on XMLHttpRequest. This is a handy javascript mechanism to request data from the originating webserver.

Every time you say something on mibbit, one of these XHR requests fires off to the Mibbit server to tell it what you said. The typing notifies, when you join a new channel, all communications is done via XHR.

Now the XHR uses straight HTTP as its protocol, which is ok, we can try and make the browser use keep-alive so that it’s all done down the same connection. But what about the headers?

 

Well, when you’re doing a lot of XHR, the headers can become a problem. We can finely tune the headers we send out in the server response, here’s what Mibbit sends back:

Connection: keep-alive

Content-Type: text/plain

Content-Length: 67

That’s it. We don’t need anything else. Note that the text/plain is needed for some browsers which assume xml and then start throwing hissy fits when it doesn’t parse as an xml document.

But what about the headers the browser sends?

The size of headers in a basic XHR request:

Firefox3: 495 bytes

IE7: 289 bytes

Safari: 357 bytes

That means if your little ajax request is simply sending 5 bytes, you’re still going to send a few hundred bytes. Wasting time, bandwidth, and processing. For XHR requests, you often simply do not need half of those headers. I already know what the user-agent is, and can send it at the start of the comms if needed. I don’t need an Accept header, because it’s irrelevant. The XHR is going to send back text. etc

We can do a little bit better… Here are the numbers when we add a few lines of js to cut down headers.

Firefox 3: 268 bytes

IE7: 259 bytes

Safari: 322 bytes

 

The code:

setHeaders = function(xhr) {

    function safeSet(k, v) {
        try {
            xhr.setRequestHeader(k, v);
        } catch(e) {}
    }

    safeSet("User-Agent", null);
    safeSet("Accept", null);
    safeSet("Accept-Language", null);
    safeSet("Content-Type", "M");
    safeSet("Connection", "keep-alive");
    safeSet("Keep-Alive", null);
}

All the code does is try as hard as it can to remove headers, or set them to small values. So instead of firefox 3 sending its massive user-agent string to your server *every* *single* *time*, it doesn’t send it at all :)

There may be other headers that you can remove, if there are extra headers sent. I’m sure the code above can also be improved to take account of different browsers.

Also note that the mileage really does vary from browser to browser. The code above actually increases the headers in opera, as it appends the values. eg User-Agent = null, Opera……

The lack of some headers may affect web proxies possibly – that should be tested. It would be possible to detect such a case at runtime and leave the header in though.

 

The w3 spec regarding this can be found here http://www.w3.org/TR/XMLHttpRequest/

 

Mibbit handles an average of about 600 XHR requests a second. That means if everyone is using firefox3, the saving would be 136kbytes/second – That’s 339GB per month!

All in all, this will reduce bandwidth usage for Mibbit and make things just that bit better :)

No related posts.

9 thoughts on “Optimizing HTTP headers for ‘ajax’ webapps

  1. Thanks for sharing that! Quite interesting, and makes you start wondering where else one can improve efficencies.

  2. Excellent post, very interesting and useful, will implement it’s wisdom on my next ajax app

    (found the post via reddit btw, look forward to exploring your blog based on this post!)

  3. Very interesting post – those who try to specify XMLHttpRequest have been discussing how to deal with the value “null” in this context. You’ve presented quite a strong argument for using null to remove headers that would otherwise be sent. (Likely some headers should be sent anyway because of security implications – e.g. content-length – but by your bandwidth calculations there is a strong use case for allowing removal of the others. Especially if some browsers already support it.)

  4. Have you done any tests with Google Chrome? I know it was advertised as being a browser designed to better handle JavaScript applications. When using Mibbit, I tend to use Chrome. I’m curious how it ranks up when compared to other browsers that use Mibbit.

  5. We’re investigating implementing this technique in the javascript client for Meteor, a comet server. Question: Have you come across any way of suppressing the Cookie header from XMLHTTPRequests? This is generally responsible for the majority of request weight, and sometimes there’s not much you can do about it (if you’re operating a subdomain of a major news site, for example, where the admins of the core site see fit to drop billions of cookies)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>