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.

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>