Click here to Skip to main content
Click here to Skip to main content

HTTP Handler to Combine Multiple Files, Cache and Deliver Compressed Output for Faster Page Load

By , 28 Aug 2008
 
Prize winner in Competition "Best ASP.NET article of August 2008"

Introduction

It's a good practice to use many small JavaScript and CSS files instead of one large JavaScript/CSS file for better code maintainability, but bad in terms of website performance. Although you should write your JavaScript code in small files and break large CSS files into small chunks, when a browser requests those JavaScript and CSS files, it makes one HTTP request per file. Every HTTP request results in a network roundtrip from your browser to the server and the delay in reaching the server and coming back to the browser is called latency. So, if you have four JavaScripts and three CSS files loaded by a page, you are wasting time in seven network roundtrips. Within the USA, latency is average 70ms. So, you waste 7x70 = 490ms, about half a second of delay. Outside USA, average latency is around 200ms. So, that means 1400ms of waiting. The browser cannot show the page properly until CSS and JavaScripts are fully loaded. So, the more latency you have, the slower the page loads.

How Bad is Latency

Here's a graph that shows how each request latency adds up and introduces significant delay in page loading:

image_16.png

You can reduce the wait time by using a CDN. Read my previous blog post about using CDN. However, a better solution is to deliver multiple files over one request using an HttpHandler that combines several files and delivers as one output. So, instead of putting many <script> or <link> tags, you just put one <script> and one <link> tag, and point them to the HttpHandler. You tell the handler which files to combine and it delivers those files in one response. This saves browser from making many requests and eliminates the latency.

image_18.png

Here you can see how much improvement you get if you can combine multiple JavaScripts and CSS into one.

In a typical web page, you will see many JavaScripts referenced:

<script type="text/javascript" src="http://www.msmvps.com/Content/JScript/jquery.js">
</script>
<script type="text/javascript" src="http://www.msmvps.com/Content/JScript/jDate.js">
</script>
<script type="text/javascript"
    src="http://www.msmvps.com/Content/JScript/jQuery.Core.js">
</script>
<script type="text/javascript"
    src="http://www.msmvps.com/Content/JScript/jQuery.Delegate.js">
</script>
<script type="text/javascript"
    src="http://www.msmvps.com/Content/JScript/jQuery.Validation.js">
</script>

Instead of these individual <script> tags, you can use only one <script> tag to serve the whole set of scripts using an Http Handler:

<script type="text/javascript" 
    src="HttpCombiner.ashx?s=jQueryScripts&t=text/javascript&v=1" >
</script>

The HTTP Handler reads the file names defined in a configuration and combines all those files and delivers them as one response. It delivers the response as gzip compressed to save bandwidth. Moreover, it generates a proper cache header to cache the response in the browser cache, so that, the browser does not request it again on future visits.

In the query string, you specify the file set name in the "s" parameter, then the content type in the "t" parameter and a version number in "v" parameter. As the response is cached, if you make changes to any of the files in the set, you will have to increase the "v" parameter value to make browsers download the response again.

Using this HttpHandler, you can deliver CSS as well:

<link type="text/css" rel="stylesheet"
 href="HttpCombiner.ashx?s=CommonCss&t=text/css&v=1" ></link>

Here's how you define the sets in web.config:

<appSettings>
    <add key="jQueryScripts" 
        value="~/Content/JScript/jquery.js,
            ~/Content/JScript/jDate.js,
            ~/Content/JScript/jQuery.Core.js,
            ~/Content/JScript/jQuery.Delegate.js,
            ~/Content/JScript/jQuery.Validation.js"    
    />
    <add key="CommonCss"
        value="~/App_Themes/Default/Theme.css,
            ~/Css/Common.css,
            ~/Controls/Grid/grid.css"
    />
</appSettings>

Example Website that Uses HttpCombiner

I have made a simple test website to show you the use of HttpCombiner. The test website has two CSS and two JS files. The default.aspx requests both of them using only one <link> and <script> tag via the HttpCombiner.ashx.

image_4.png

Here's the content of the Default.aspx:

image_12.png

Here you see, there's one <link> tag sending a request to HttpCombiner.ashx to deliver the set named Set_Css and a <script> tag asking for a set Set_Javascript.

Files that belong to these two sets are defined in the web.config file:

image_30.png

Here's how the handler works:

  • First it reads the file set name passed in the "s" parameter
  • Then it gets the files defined in the web.config for the set
  • It reads individual files and stores in a buffer
  • The buffer is then gzip compressed
  • The compressed buffer is sent to the browser
  • The compressed buffer is stored in ASP.NET cache so that subsequent requests for the same set can be directly served from cache without reading the individual files from file system or external URL

Benefits of the handler:

  • It saves network roundtrip. The more files you can put in one set, the more you save in network latency. This improves performance.
  • It caches the total combined response as compressed and thus saves reading file from file system and compressing it again and again. This improves scalability.

How the HttpHandler Works

First the handler reads the set, type and version to use from the query string:

image_20.png

If the set has already been cached, the it's written directly from cache. Otherwise the files in the set are loaded one by one and stored in a MemoryStream. The MemoryStream is compressed using GZipStream if browser supports compressed output.

image_22.png

After combining all the files and compressing it, the combined bytes are cached so that subsequent requests can be directly served from cache.

image_24.png

The GetFileBytes function reads a file or URL and returns the bytes. So, you can use Virtual Path to files within your website or you can use URL to external Javascript/CSS files hosted on another domain.

image_26.png

The WriteBytes function has much wisdom in it. It generates a proper header based on whether the bytes are in compressed form or not. Then it generates proper browser cache header to make browser cache the response.

image_28.png

How to use this handler::

  • Include the HttpCombiner.ashx in your project
  • Define the file sets in the <appSettings> section of your web.config
  • Change <link> and <script> tags throughout your website to point to HttpCombiner.ashx in this format:
    HttpCombiner.ashx?s=<setName>&t=<contentType>&v=<versionNo>

Conclusion

That's it! Make your website faster to load, get more users and earn more revenue.

You should also read my earlier articles about deferring and combining script loading for faster perceived speed and how to make best use of browser cache.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Omar Al Zabir
Architect BT, UK (ex British Telecom)
United Kingdom United Kingdom
Member

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionSlight improvement to the authors excellent ideamemberNorman Solomon19 May '13 - 0:52 
QuestionVersion updatemembersafiyullah12 Apr '13 - 0:11 
GeneralMy vote of 5membersmfarooq5 Nov '12 - 22:14 
QuestionAll CSS and JS are not being loadedmemberAmita Bhakkad18 Sep '12 - 21:00 
AnswerRe: All CSS and JS are not being loadedmemberOmar Al Zabir19 Sep '12 - 10:59 
GeneralRe: All CSS and JS are not being loadedmemberfarooq84022 Jan '13 - 2:31 
GeneralRe: All CSS and JS are not being loadedmemberfarooq84028 Jan '13 - 0:47 
QuestionIts really nice idea .... butmemberMuhammad Shoaib Raja29 Jun '11 - 21:14 
AnswerRe: Its really nice idea .... butmvpOmar Al Zabir4 Jul '11 - 7:31 
Generalfyi ...memberMichael Stilson7 Apr '11 - 4:47 
GeneralMy vote of 5memberJennOpp8 Feb '11 - 11:52 
QuestionHow can i achieve this in sharepoint site?memberJim Mathew27 Sep '10 - 5:31 
GeneralThere is Error in returns by the this handler [modified]memberRavenet31 Jul '10 - 3:11 
GeneralParser Errormemberrashadkk12 Jul '10 - 20:34 
GeneralA better way to serve dynamically created css/jsmembertaliesins19 Apr '10 - 3:34 
GeneralRe: A better way to serve dynamically created css/jsmvpOmar Al Zabir19 Apr '10 - 7:28 
GeneralRe: A better way to serve dynamically created css/jsmembervardars12 Aug '10 - 21:44 
GeneralNot working with Url RewritingmemberRavenet25 Dec '09 - 0:09 
GeneralRe: Not working with Url RewritingmvpOmar Al Zabir25 Dec '09 - 0:13 
GeneralRe: Not working with Url RewritingmemberRavenet25 Dec '09 - 0:28 
GeneralRe: Not working with Url RewritingmemberRavenet25 Dec '09 - 0:43 
GeneralMinify and obfuscate with YUI CompressormemberKellros5 Oct '09 - 1:59 
QuestionCannot update file after cachingmemberMember 53116015 Sep '09 - 4:17 
AnswerRe: Cannot update file after cachingmvpOmar Al Zabir15 Sep '09 - 5:42 
GeneralRe: Cannot update file after cachingmemberMember 53116015 Sep '09 - 6:17 
GeneralMedium TrustmemberZach Floyd4 Sep '09 - 6:21 
QuestionWhat is the difference between Scripts.ashx & HttpCombiner.ashx?memberMember 19851656 Jun '09 - 0:00 
Generalfirefox 3 issues with combining CSS filesmemberswyche6 Feb '09 - 5:51 
GeneralRe: firefox 3 issues with combining CSS filesmemberkomemi4 Jan '10 - 9:17 
GeneralDynamic list of filesmemberkitkatrobins18 Jan '09 - 22:35 
GeneralUnable to download the codemembermaxcom5 Dec '08 - 22:22 
QuestionHow to implement your code if using friendly adapters and ajax net framework?memberMember 325727511 Sep '08 - 12:58 
GeneralReal Greatmembercwp424 Sep '08 - 22:23 
QuestionAre there any benchmarks?memberFilipe Martins3 Sep '08 - 5:43 
AnswerRe: Are there any benchmarks?memberOmar Al Zabir4 Sep '08 - 1:24 
GeneralBackground ImagesmembernovelProjects3 Sep '08 - 3:41 
QuestionHow about including Javascript and CSS compressor/minifier/obfuscator ?memberseaplanner3 Sep '08 - 0:09 
GeneralCombine CSS with many different themesmemberMember 34235452 Sep '08 - 3:06 
GeneralRe: Combine CSS with many different themesmembermmengel8 Sep '08 - 5:50 
GeneralDevelopment ProcessmemberMember 31061881 Sep '08 - 4:03 
GeneralMicrosoft JScript runtime error object expectedmemberMember 310618831 Aug '08 - 7:40 
GeneralRe: Microsoft JScript runtime error object expectedmemberMember 31061881 Sep '08 - 3:40 
GeneralExisting featurememberPrzemyslaw Celej30 Aug '08 - 9:39 
GeneralRe: Existing featurememberMarc Leger31 Aug '08 - 11:32 
GeneralInterestingmemberN a v a n e e t h30 Aug '08 - 8:47 
GeneralInteresting ideasmemberOlivier Giulieri28 Aug '08 - 13:05 
GeneralBrowsers/proxies won't cache resources with URLs containing a QueryStringmemberAndrew Davey28 Aug '08 - 12:30 
GeneralRe: Browsers/proxies won't cache resources with URLs containing a QueryStringmemberOmar Al Zabir28 Aug '08 - 19:11 
GeneralRe: Browsers/proxies won't cache resources with URLs containing a QueryStringmemberAndrew Davey28 Aug '08 - 22:05 
GeneralRe: Browsers/proxies won't cache resources with URLs containing a QueryStringmemberWaldenL3 Sep '08 - 3:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 28 Aug 2008
Article Copyright 2008 by Omar Al Zabir
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid