Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Package that speeds up loading of JavaScript, CSS and image files

0.00/5 (No votes)
29 Mar 2012 3  
Improves web site performance by combining and minifying JavaScript and CSS files on the fly. Processes ASP.NET AJAX toolkit .axd files too. Improves image caching and loading. Very easy to add to any ASP.NET web site.

Introduction

Most web pages include one or more JavaScript files, CSS files and or images (loaded from image tags or from CSS files). If your pages use ASP.NET AJAX toolkit controls, they will be loading .axd files as well. The CombineAndMinify package presented here automatically speeds up the loading of these files into your pages, and reduces the bandwidth spent on loading those files. You'll see how it does that when reading the list of features. The result can be a dramatic improvement in web site performance.

There is no need to change the code of your web site to use CombineAndMinify. You only need to add a dll and update your web.config. Step by step installation instructions are in the installation section.

If you like this article, please vote for it.

Contents

Requirements

  • ASP.NET 4 or higher.
  • To compile the included source code, you need any version of Visual Studio 2010. You don't need this if you simply use the binaries.
  • IIS 6 or higher for your live site.

Features

The features below can all be switched on and off individually via the web.config file (full description). If you just install CombineAndMinify and not do any further configuration, it only minifies and combines JavaScript and CSS files.

  • Minifies JavaScript and CSS files. Minification involves stripping superfluous white space and comments. Only JavaScript and CSS files that are loaded from the head sections of your pages are minified.

  • Correctly processes files that contain non-English text such as Chinese. CombineAndMinify uses the efficient YUI minifier to minify JavaScript and CSS files that are all in English (that is, contain only ASCII characters). Because the YUI minifier doesn't handle non-ASCII characters (such as Chinese), CombineAndMinify automatically uses the JSMIN algorithm for files containing such characters. JSMIN is less efficient, but handles non-ASCII characters well.

  • Combines JavaScript files and CSS files. Loading a single large file is often much quicker than loading a series of small files, because that saves the overhead involved in all the request and response messages.

    If a CSS file contains image urls that are relative to the folder containing the CSS file itself, those urls are fixed up by CombineAndMinify. That way, they continue to work even if CSS files from several different folders are combined.

  • Can be used with sites hosted on low cost shared hosting plans, such as GoDaddy's.

  • Processes .axd files as used by the ASP.NET AJAX toolkit (details).

  • Allows you to configure CombineAndMinify so it only kicks in in release mode. That way, you see your individual files complete with white space and comments while developing, and reap the performance improvement in your live site.

  • Reduces the size of the HTML generated by your .aspx pages by removing white space and comments. Note that the .aspx files themselves are not affected, only the HTML sent to the browser.

  • Allows you to configure cookieless domains from which to load JavaScript files, CSS files and images. This way, the browser no longer sends cookies when it requests those files, reducing wait times for the visitor.

  • Lets you configure multiple cookieless domains. This causes the browser to load more JavaScript, CSS and image files in parallel.

  • Optimizes use of the browser cache by allowing the browser to store JavaScript, CSS and image and font files for up to a year. Uses version ids in file names to ensure the browser picks up new versions of your files right away, so visitor never see outdated files (details on how this works).

  • Unlike similar packages, doesn't add query strings when combining files or when inserting versions. This optimizes caching by proxy servers (many proxy servers won't cache files with query strings).

  • Converts image file names to lower case, to make it easier for those proxy and browser caches that do case sensitive file name comparisons to find your file in their caches - so they don't request the same file again.

  • Preloads images immediately when the page starts loading, instead of when the browser gets round to loading the image tags - so your images appear quicker. You can give specific images priority.

  • Helps you detect missing files by throwing an exception when a JavaScript file, CSS file or image is missing. By default, CombineAndMinify handles missing files silently, without throwing an exception.

  • To reduce CPU overhead and disk accesses caused by CombineAndMinify, it caches intermediate results, such as minified files. A cache entry is removed when the underlying file is changed, so you'll never serve outdated files.

This CombineAndMinify package is just one way of improving the performance of your web site. My recently released book ASP.NET Performance Secrets shows how to pinpoint the biggest performance bottlenecks in your web site, using various tools and performance counters built into Windows, IIS and SQL Server. It then shows how to fix those bottlenecks. It covers all environments used by a web site - the web server, the database server, and the browser. The book is extremely hands on - the aim is to improve web site performance today, without wading through a lot of theory first.

Server compression

If you are interested in web site performance, you may be interested in this short digression into server compression.

IIS 6 and 7, and Apache as well, provide the option to gzip compress all text files (html, JavaScript, CSS, etc.) sent to the browser. All modern browsers know how to decompress those files. Compression can save you a lot of bandwidth and download time. It is not uncommon to reduce file sizes by way over 50%.

In IIS, compression is switched off by default for dynamic files, such as .aspx files. This is because it increases the load on the CPU. However, with the overabundence of CPU cycles on modern server hardware, switching on compression for dynamic files on your server is almost always a great idea. Also, IIS 6 and 7 allow you to set the compression level, so you can choose a level that you're comfortable with. Finally, IIS 7 can automatically switch off compression when CPU usage goes over a predetermined level (set by you), and switch it back on after CPU usage has dropped below a second level (also set by you). It even lets you cache compressed dynamic files, which makes compression extremely attractive.

Switching on basic compression on IIS 7 is easy, but getting the most out of it is a bit tricky. Switching on compression in IIS 6 is just tricky. Good places to find out more would be here (for IIS 7) and here (for IIS 6).

Or you could read chapter 10 of my book ASP.NET Performance Secrets where it is all spelt out in one place (believe me, this will save you a lot of time).

How it works

When a page is generated by the server, ASP.NET invokes CombineAndMinify while processing the HEAD element of the page. Depending on how you configured CombineAndMinify in web.config, it then minifies and combines CSS and JavaScript files, rewrites image urls to include version ids and cookieless domains, etc.

The minified and/or combined CSS and JavaScript files are written to a separate folder under your web site's root. CombineAndMinify will modify the html being sent to the server, so link and script tags referring to the original files are automatically updated to point to these generated files. Similarly, if you let CombineAndMinify insert version ids in image file names to enable far future caching, CombineAndMinify makes copies of the images with the new file names.

Note that your source code does not get changed by CombineAndMinify. It kicks in while ASP.NET processes the page and modifies the final html that is going to be sent to the browser.

The folder for the generated files will be created if it doesn't exist. By default, it is named "___generated". You can change that name with the generatedFolder attribute.

To minimise CPU overhead, CombineAndMinify uses caching to remember whether it needs to generate new files, etc. To ensure you never serve outdated content, it uses file dependencies to ensure that when any of your files are changed, any dependent generated files are updated immediately. Also, if a generated file is somehow deleted, it is automatically regenerated.

If you do not want CombineAndMinify to generate files on the file system, you can tell it to keep the files in cache using the enableGeneratedFiles attribute. You then need to configure an HTTP Handler (built into CombineAndMinify) in your web.config to serve this cached content. The description of the enableGeneratedFiles attribute shows how to do this.

Installation

  1. Compile CombineAndMinify:
    1. Download the zip file with the source code, and unzip in a directory.
    2. You will find the files CombineAndMinify.dll, EcmaScript.NET.modified.dll and Yahoo.Yui.Compressor.dll in the CombineAndMinify\bin folder.
    3. If you're interested in the source code, open the CombineAndMinify.sln file in Visual Studio 2010 or later. You'll find that the sources are organized in a solution, with these elements:
      1. Project CombineAndMinify is the actual CombineAndMinify package.
      2. Web site Testsite contains a lot of (functional but rather disorganised) test cases. Ignore this unless you want to test CombineAndMinify.
  2. Update your web site:
    1. Add a reference to CombineAndMinify.dll to your web site (in Visual Studio, right click your web site, choose Add Reference)
    2. Add the custom section combineAndMinify to your web.config:
      <configuration>
          ...
          <configSections>
              ...
              <section name="combineAndMinify" type="CombineAndMinify.ConfigSection" requirePermission="false"/> 
              ...
          </configSections>
          ...
      </configuration>

  3. Allow CombineAndMinify to process the head sections of your pages. That way, it can replace the tags loading individual JavaScript and CSS files with tags loading combined files:

    1. Make sure that the head tags of your pages have runat="server", like so:

      <head runat="server">

      When you create a new page in Visual Studio, it creates a head section with runat="server", so you probably don't have to change anything here.

    2. Add a folder called App_Browsers to the root folder of your web site.
    3. Use your favorite text editor (such as Notepad) to create a text file in the App_Browsers folder. Call it HeadAdapter.browser. Into that file, copy and paste this code:
      <browsers>
        <browser refID="Default">
          <controlAdapters>
            <adapter controlType="System.Web.UI.HtmlControls.HtmlHead"
               adapterType="CombineAndMinify.HeadAdapter" />
          </controlAdapters>
        </browser>
      </browsers>
      This tells ASP.NET to leave processing of all HtmlHead controls (which represent the head tags) to the class CombineAndMinify.HeadAdapter (which is part of CombineAndMinify).

Combining .axd files

You can skip this section if you do not use the ASP.NET AJAX toolkit.

How .axd files are loaded

Before discussing how you can combine and minify .axd files with CombineAndMinify, lets recap quickly where .axd files come in when a visitor loads your pages.

When you use controls provided by the ASP.NET AJAX toolkit on a page, the toolkit ensures that a number of .axd files are loaded along with the page. These contain the resources needed by the controls:

  1. CSS - To load CSS definitions required by the controls you used on the page, it inserts link tags in the head of the page. These all include the file WebResource.axd and a query string to identify which CSS definitions to send to the browser.

  2. JavaScript - To load the JavaScript required by the controls on the page, it inserts script tags in the body of the page. These may load a mixture of WebResource.axd files, ScriptResource.axd files and the .aspx page itself. Again, a query string is used to identify what JavaScript to send to the browser.

  3. Images - To load any images required by the controls, the JavaScript dynamically creates image tags that load the images. The sources of these image tags are WebResource.axd files, with a query string to identify the image to send to the browser.

Instead of storing the .axd files on the file system of your server, the ASP.NET AJAX toolkit keeps them in the .dll files that you added to your site when you installed the toolkit. An HTTP Handler intercepts requests for .axd files from the browser and reads the required CSS, JavaScript or image data from the .dll files, based on the query string sent with the request.

How .axd files are combined

Here is how to combine and minify the .axd files:

  1. CSS - CombineAndMinify will process the .axd files with CSS definitions along with the other CSS files.

    Because these files do not physically exist on the web server's file system, CombineAndMinify retrieves their content via a request to the web server instead of reading from the server's file system. Also, when the insertVersionIdInImageUrls configuration option is used to get the browser to re-request script and CSS files that have been updated on the server, a dummy version id is used for .axd files - again because they are not physical files. Apart from that, .axd files are first class citizens that benefit from minification, far future cache expiry and the other features provided by CombineAndMinify.

    This means that CombineAndMinify may combine .axd files with other CSS files. Also, because CombineAndMinify generates its own link tags which all use .js files, you will find no more link tags with .axd files in the head of your pages. The CSS required for the controls will still be loaded, but it will be requested by the browser as .js files rather than .axd files.

    To prevent CombineAndMinify from processing .axd files, set the configuration option enableAxdProcessing to false (it is true by default).

  2. JavaScript - From ASP.NET 3.5 onwards, you can combine most .axd files with JavaScript by simply replacing the ScriptManager control with the ToolkitScriptManager control:

    <%@ Page ... %>
    <%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajaxToolkit" %>
    ...
    <head runat="server">
    ...
    </head>
    <body>
        ...
        
        <%-- Use ToolkitScriptManager instead of ScriptManager --%>    
        <ajaxToolkit:ToolkitScriptManager ID="scriptManager" runat="server" ScriptMode="Release" CombineScripts="true"  />

    If you use .Net 4, you'll find that this minifies the JavaScript as well. Note that the ToolkitScriptManager control only combines the .axd files with the JavaScript, not those with the CSS definitions. It is the CombineAndMinify package that will take care of the .axd files with the CSS definitions.

    More information about the ToolkitScriptManager control is at:

  3. Images - To reduce the number of requests for images, the JavaScript used to implement the controls would need to be modified to for example use CSS sprites. Hopefully, Microsoft will do this one day in the not too distant future.

Additional configuration when using a shared hosting plan

If you use a low cost shared hosting plan for your site, you may find that controls from the ASP.NET AJAX toolkit no longer work well when you use CombineAndMinify.

This is because when CombineAndMinify retrieves .axd files to combine them, it does so by sending web requests to the web server - it has to, because .axd files are not actually stored as files in the file system (they are packed in the toolkit dll files). However, many shared hosting plans run sites in trust level Medium, preventing CombineAndMinify from sending those requests.

You can solve this by adding the following line to the <system.web> section of your web.config (replace yourdomain\.com with your own domain):

<system.web>
    <trust level="Medium" originUrl="http://yourdomain\.com/.*" />
</system.web>

The value of originUrl is a regular expression. The backslash before the .com is not a typo - it is part of the regular expression. The .* at the end matches all files in your site.

This will only make your ASP.NET AJAX toolkit controls and CombineAndMinify work well together when your site is live though. In your development environment, your site may be known by something like localhost:32100, which doesn't match the originUrl.

This is normally not an issue, because by default CombineAndMinify is only active in release mode when you have debug="false" in your web.config (see configuration setting active).

If you decide to activate CombineAndMinify in your development environment - with debug="true" in your web.config and active set to Always - you need to set originUrl to a regular expression that matches your site's domain in your development environment:

<system.web>
    <trust level="Medium" originUrl="http://localhost:\d{1,5}/.*" />
</system.web>

This matches domain localhost with a port that has 1 to 5 digits.

Using shared hosting

From version 1.6, CombineAndMinify can be used with hosting companies that run your site with trust level Medium, such as GoDaddy. As a result, you should be able to use it with any hosting company that supports ASP.NET.

What is trust level Medium? All ASP.NET sites are given a trust level. If your site has trust level Full - such as in your development environment - it can basically do anything it likes on the web server. Because this may impact other sites on the same web server, many hosting companies give your site trust level Medium if you use one of their shared hosting plans (where you site shares a server with other sites).

This means that your C# or VB.Net code can't do all sorts of things that in Microsoft's opinion may impact on other sites on the same server. As a result, you may find that code that runs fine in your local development environment will no longer work when you try to take it live. Unless you develop with trust level Medium in mind from the start, you could spend a while making it work in the shared hosting environment.

To prevent these problems, if you are developing a site that will use a shared hosting plan, you should give your site trust level Medium even in your development environment. That way, you catch any issues early. You can achieve this by including a <trust> tag in the <system.web> section of your web.config file:

<system.web>
    <trust level="Medium" />
</system.web>

Configuration

By default, CombineAndMinify minifies and combines JavaScript and CSS files. To use the other features, or to switch off minification or combining of JavaScript or CSS files, add a combineAndMinify element to your web.config file, like so:

<configuration>
    ...
    <combineAndMinify ... >

    </combineAndMinify>
    ...
</configuration>

The combineAndMinify element supports these attributes and child elements:

active

Determines when CombineAndMinify is active. When it is not active, it doesn't affect your site at all and none of the other attributes or child elements listed in this section have any effect.

Value Description
Never Package is never active, irrespective of debug mode
Always
(default)
Package is always active, irrespective of debug mode
ReleaseModeOnly Package is only active in release mode
DebugModeOnly Package is only active in debug mode

Example

<configuration>
    ...
    <combineAndMinify active="ReleaseModeOnly" >

    </combineAndMinify>
    ...
</configuration>

Whether your site is in debug or release mode depends on the debug attribute of the compilation element in your web.config file. If that attribute is set to false, your site is in release mode (as it should be when live). When it is set true, it is in debug mode (as it should be in development). It looks like this in web.config:

<configuration>
    ...
    <system.web>
        <compilation debug="true">
            ...
        </compilation>
        ...
    </system.web>
    ...
</configuration>

You may find it easier to debug your site with CombineAndMinify deactivated - minified JavaScript files are not easy to read. To ensure that CombineAndMinify is active only in Release mode, set active to ReleaseModeOnly, as shown in the example above.

Note that the active attribute acts as a master switch for the whole CombineAndMinify package. It's like the ignition in a car - if you don't turn on the ignition (or at least the battery), pressing any other buttons on the dashboard won't do anything.

combineCSSFiles

Determines whether CombineAndMinify combines CSS files.

Value Description
None CSS files are never combined
PerGroup
(default)
CSS files are combined per group. See explanation below.
All All CSS files are combined into a single CSS file.

Example

<configuration>
    ...
    <combineAndMinify combineCSSFiles="All" >

    </combineAndMinify>
    ...
</configuration>

To see what is meant with "per group", have a look at this example:

<link rel="Stylesheet" type="text/css" href="/css/site1.css" />
<link rel="Stylesheet" type="text/css" href="/css/site2.css" />

<script type="text/javascript" src="js/script1.js"></script> 

<link rel="Stylesheet" type="text/css" href="/css/site3.css" />
<link rel="Stylesheet" type="text/css" href="/css/site4.css" />

By default, CombineAndMinify tries to ensure that the order of the JavaScript and CSS definitions doesn't change when JavaScript and CSS files are combined. It does this by grouping CSS files whose tags are after each other. In this case, there are 2 groups - site1.css and site2.css, and site3.css and site4.css. This causes the browser to load the CSS and JavaScrip definitions in the exact same order as when the files had not been combined:

  1. Combined file with all CSS definitions in site1.css and site2.css
  2. script1.js
  3. Combined file with all CSS definitions in site3.css and site4.css

You get this behaviour when you set combineCSSFiles to PerGroup (which is the default). CombineAndMinify supports grouping for JavaScript files as well, which is controlled by the combineJavaScriptFiles attribute.

Combining all CSS files into one file

If you set combineCSSFiles to All, all CSS files get combined into a single file. The link tag to load that combined CSS file gets placed where the link tag of the first CSS file used to be. In our example, that causes this load order:

  1. Combined file with all CSS definitions in site1.css, site2.css, site3.css and site4.css
  2. script1.js

As you see, the CSS definitions in site3.css and site4.css now get loaded before script1.js, instead of after script1.js. As a result, the number of CSS files that need to be loaded is reduced from two to one (as compared with grouping), but you also change the order in which CSS and JavaScript definitions are loaded. It depends on your site whether that is an issue or not.

Loading CSS files from another site

If you decide to set combineCSSFiles to All, be sure that your site doesn't load CSS files from another web site. This is uncommon, but if yours is one of the rare sites that does this, consider this example:

<link rel="Stylesheet" type="text/css" href="/css/site1.css" />
<link rel="Stylesheet" type="text/css" href="/css/site2.css" />

<link rel="Stylesheet" type="text/css" href="http://anothersite.com/css/other.css" />

<link rel="Stylesheet" type="text/css" href="/css/site3.css" />
<link rel="Stylesheet" type="text/css" href="/css/site4.css" />

CombineAndMinify never combines CSS files from other sites - not with CSS files from your site, not with CSS files from the other site. As a result, if you change combineCSSFiles to All, CombineAndMinify will combine all CSS files loaded from your site (but not those from the other site), causing the following load order:

  1. Combined file with all CSS definitions in site1.css, site2.css, site3.css and site4.css
  2. http://anothersite.com/css/other.css

The definitions in other.css now came after those in site3.css and site4.css, meaning they may take precedence - which could break your CSS.

combineJavaScriptFiles

Determines whether CombineAndMinify combines JavaScript files.

Value Description
None JavaScript files are never combined
PerGroup
(default)
JavaScript files are combined per group. See explanation below.
All All JavaScript files are combined into a single JavaScript file.

Example

<configuration>
    ...
    <combineAndMinify combineJavaScriptFiles="All" >

    </combineAndMinify>
    ...
</configuration>

As you saw in the description of combineCSSFiles, CombineAndMinify groups JavaScript files in the same way as CSS files. Similarly, if you set combineJavaScriptFiles to All, all JavaScript files that are loaded from your site get combined into a single JavaScript file.

However, there is one major difference with combining CSS files: When you combine all JavaScript files into a single file (combineJavaScriptFiles is All), the script tag for the combined file winds up at the location where the last original script tag used to be. This in contract to a combined CSS file, which winds up at the location of the first original CSS tag.

This makes life easier if you load JavaScript libraries from extenal sites. If you use a popular CombineAndMinify package such as jQuery, you can load it from the free Google Content Delivery Network (CDN) and also from the free Microsoft CDN - which saves you bandwidth and download time (details about those CDNs are in chapter 13 of my book).

Take this example:

<script type="text/javascript" src="/js/script1.js" ></script>
<script type="text/javascript" src="/js/script2.js" ></script>

<!-- load jQuery from free Google CDN -->
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>

<script type="text/javascript" src="/js/script3.js" ></script>
<script type="text/javascript" src="/js/script4.js" ></script>

script3.js and script4.js may well be dependent on jquery.min.js. If combineJavaScriptFiles is set to PerGroup, you wind up with this load order:

  1. Combined file with all JavaScript definitions in script1.js and script2.js
  2. jquery.min.js
  3. Combined file with all JavaScript definitions in script3.js and script4.js

So all definitions load in the same order as before the files were combined, as you'd expect. Now when you set combineJavaScriptFiles to All, you wind up with this load order:

  1. jquery.min.js
  2. Combined file with all CSS definitions in script1.js, script2.js, script3.js and script4.js

This would probably still work well, because the definitions in script3.js and script4.js still get loaded after those in jquery.min.js. script1.js and script2.js now get loaded after jquery.min.js, but than jquery.min.js wouldn't have been dependent on those files anyway.

minifyCSS

Determines whether CombineAndMinify minifies CSS files.

Value Description
true
(default)
CSS files get minified
false CSS files do not get minified

Example

<configuration>
    ...
    <combineAndMinify minifyCSS="false" >

    </combineAndMinify>
    ...
</configuration>

Minifying a file means removing redundant white space and comments. This not only reduces your bandwidth usage and download times, but also makes it harder for outsiders to reverse engineer your web site. It also encourages developers to add comments to their CSS files (and especially their JavaScript files), now that those comments do not travel over the wire to the browser.

It is normally safe to minify files, so this feature is enabled by default. However, just in case removing white space or comments breaks your file, the option exists to switch it off.

Remember that CombineAndMinify doesn't minify your source files. It reads your source files, and minifies their content before sending them to the browser. To save CPU cycles, it caches the minified versions, using file dependencies to ensure cached versions are removed the moment you change the underlying files.

minifyJavaScript

Determines whether CombineAndMinify minifies JavaScript files.

Value Description
true
(default)
JavaScript files get minified
false JavaScript files do not get minified

Example

<configuration>
    ...
    <combineAndMinify minifyJavaScript="false" >

    </combineAndMinify>
    ...
</configuration>

cookielessDomains

To have your JavaScript, CSS and image files loaded from one or more cookieless domains, specify those domains using a cookielessDomains child element.

Example

<configuration>
    ...
    <combineAndMinify ... >
        <cookielessDomains>
            <add domain="http://static1.mydomain.com"/>
            <add domain="http://static2.mydomain.com/"/>
        </cookielessDomains>
    </combineAndMinify>
    ...
</configuration>

Cookieless domains help you boost web site performance in two ways. They let you cut down on the overhead created by cookies. And they let you get the browser to load more JavaScript, CSS and image files in parallel. Let's look at the cookie overhead first, and then the parallel loading.

Cookie overhead

If your site sets a cookie on the browser, then each time the browser sends a request to your site, that request contains the cookie. The issue is that the cookie is not only sent when requesting an .aspx file, but also when requesting static files, such JavaScript files, CSS files and images. In most cases, this is a waste of bandwidth (the exception would be if you have a handler that processes for example JavaScript files and that uses the cookie).

However, the browser won't send the cookie to another domain or sub domain. So if your page is at http://www.mydomain.com/page.aspx (using subdomain www), and you put your images and other static files on http://static1.mydomain.com (using subdomain static1), than the browser won't send cookies when requesting static files.

As an aside, if your site uses cookies (or ASP.NET sessions, which uses cookies), you should never allow visitors to access your pages without a subdomain. That is, don't allow them to access http://mydomain.com/page.aspx. Otherwise, if a visitor first accesses http://www.mydomain.com/page.aspx (using subdomain www), sets a cookie, and then comes back via http://mydomain.com/page.aspx (no subdomain), the browser won't send the cookie! IIS 7 makes it very easy to redirect requests to http://mydomain.com to http://www.mydomain.com using an entry in web.config. See Microsoft's iis.net, or the image control adapter included in chapter 12 of my book ASP.NET Site Performance Secrets.

Parallel loading

When a browser loads a page, it loads the static files (images, JavaScript files, CSS files) in parallel to reduce the visitor's wait time. However, the browser limits the number of files that are loaded in parallel. Modern browsers (IE7 and better, Firefox, Chrome, Safari) have a limit of about 6, while older browsers (such as IE6) have a limit of 2.

However, this limit is per (sub)domain. It isn't per IP address. This means that if you spread your static files over for example 2 cookieless domains, you allow the browser to load up to two times more files in parallel.

Using cookieless domains on your site

If you add a cookielessDomains element with one or more domains to the combineAndMinify element, CombineAndMinify adds those domains to the urls of all static files in your site. This includes images referenced from CSS files.

This means that if you use:

<configuration>
    ...
    <combineAndMinify ... >
        <cookielessDomains>
            <add domain="http://static1.mydomain.com"/>
        </cookielessDomains>
    </combineAndMinify>
    ...
</configuration>

Then for example

<img src="images/ball3.png" height="10" width="10" />

is replaced by

<img src="http://static1.mydomain.com/images/ball3.png" height="10" width="10" />

If you define multiple domains, such as:

<configuration>
    ...
    <combineAndMinify ... >
        <cookielessDomains>
            <add domain="http://static1.mydomain.com"/>
            <add domain="http://static2.mydomain.com"/>
        </cookielessDomains>
    </combineAndMinify>
    ...
</configuration>

Then CombineAndMinify attempts to spread the files over the available domains (note that you can add more than 2 domains if you want). This to get the browser to load more files in parallel. So if you have these image tags:

<img src="images/ball3.png" />
<img src="images/woodentoy4.png" />

You would wind up with:

<img src="http://static2.mydomain.com/images/ball3.png" />
<img src="http://static1.mydomain.com/images/woodentoy4.png" />

Log into your DNS name server to create the subdomains. If your site is hosted by an external hosting company, their control panel will probably let you create subdomains - if in doubt, ask them. Create the static1, static2, etc. subdomains (you can use any subdomain names you like). Make sure they point to the same IP address as your www subdomain. This way, you don't have to physically move your static files. Note that every subdomain that is not your www subdomain acts as a "cookieless" subdomain - it isn't like there are special "cookieless" subdomains as such.

Contrary to what you may think, if you have say 2 cookieless domains, CombineAndMinify won't use one domain for half the static files and the other domain for the other half. This is because it needs to make sure that on every page, a given static file is always given the same domain. If images/ball3.png were turned into http://static1.mydomain.com/images/ball3.png on one page, but to http://static2.mydomain.com/images/ball3.png on a second page, than the browser won't find ball3.png in its cache when it hits the second page, even if it stored ball3.png when it accessed the first page. Because of the different domains, it would regard the two urls as different, even though they actually point at the same resource.

Because of this requirement, CombineAndMinify uses the hash code of the file name to work out which domain to use. So if there are two domains, than if the hash is even it uses the first domain, and if it is odd it uses the second domain. Because it is unlikely that 50% of file names have an even hash code, you are unlikely to get a perfect distribution of the static files over the available domains.

enableCookielessDomains

Determines whether cookieless domains are used.

Value Description
Never Cookieless domains are never used.
Always
(default)
Cookieless domains are always used, provided that 1) CombineAndMinify is active, and 2) you have defined a cookielessDomains element with cookieless domains.
ReleaseModeOnly Cookieless domains are only used in release mode.
DebugModeOnly Cookieless domains are only used in debug mode.

Example

<configuration>
    ...
    <combineAndMinify active="Always" enableCookielessDomains="ReleaseModeOnly" >
        <cookielessDomains>
            <add domain="http://static1.mydomain.com"/>
            <add domain="http://static2.mydomain.com"/>
        </cookielessDomains>
    </combineAndMinify>
    ...
</configuration>

This option is really only useful if you decide to activate CombineAndMinify in your development environment. In that case, you may decide to only use cookieless domains in release mode, while using all the other features of CombineAndMinify in both release and debug mode.

The reason for this is that if you have new images in your development environment that are not yet on your live site, than they won't show up in your development environment if you use the cookieless domains - which point to your live site.

If you want to take that route, set enableCookielessDomains to ReleaseModeOnly.

The default value for enableCookielessDomains is Always. However, keep in mind that for CombineAndMinify to use cookieless domains, it has to be active. And by default, it is only active in release mode.

preloadAllImages

Determines whether CombineAndMinify inserts code that preloads all images when the page starts loading.

Value Description
true All images are preloaded
false
(default)
No images are preloaded (except for those specified using child element prioritizedImages, described further below)

Example

<configuration>
    ...
    <combineAndMinify preloadAllImages="true" >

    </combineAndMinify>
    ...
</configuration>

Normally, the browser only starts loading an image after it has encountered its image tag in the HTML or in a CSS file. If the image tag is towards the end of a big page, or if it takes a while to load the CSS file, it can take a while before image loading starts.

To get the browser to start loading all images immediately when the page itself starts loading, set the preloadAllImages attribute to true. CombineAndMinify than generates JavaScript at the start of the page head to load each image into the browser cache, along these lines:

<script type="text/javascript">
var img0=new Image();img0.src='http://www.codeproject.com/images/ball3.png';
var img1=new Image();img1.src='http://www.codeproject.com/css/chemistry.png';
var img2=new Image();img2.src='http://www.codeproject.com/images/toy4.png';
...
</script>

Now when the browser encounters an image tag, the image is already in browser cache, so the browser can show the image right away.

prioritizedImages

Allows you to prioritize certain images for preloading.

Example

<combineAndMinify ... >
    <prioritizedImages>
        <add url="/images/salesbutton.png"/>
        <add url="/images/logo.png"/>
    </prioritizedImages>
</combineAndMinify>

If you have many images on your pages, you may want to prioritize certain images. For example, if your "order now" button is an image, you want that image in front of your visitors as soon as possible.

You can use prioritizedImages without setting preloadAllImages to true. Here is how these two attributes interact:

prioritizedImages preloadAllImages Images preloaded
Empty or not present true All the images referred to from CSS files and all the images on the page are preloaded. They are loaded in the order in which their tags appear in the CSS or in the HTML. Images referred to from CSS are preloaded before images on the page itself.
Empty or not present false None
One or more urls true All urls listed in prioritizedImages are preloaded first, than all the other images.
One or more urls false Only the urls listed in prioritizedImages are preloaded

You can list any image urls in prioritizedImages, not just urls of images that are used on the page. If you know which page visitors are likely to go to next, you could use this to preload the images used on that next page.

makeImageUrlsLowercase

Determines whether CombineAndMinify makes all image urls lowercase.

Value Description
true All images urls are converted to lowercase
false
(default)
Images url casing is left as it is

Example

<configuration>
    ...
    <combineAndMinify makeImageUrlsLowercase="true" >

    </combineAndMinify>
    ...
</configuration>

You may be using inconsistent casing in your web pages to refer to the same image. For example:

<img src="/images/woodentoy4.png" height="10" width="10" />
...
<img src="/images/WoodenToy4.png" height="10" width="10" />

Assume a browser or proxy loads woodentoy4.png and stores it in its cache. When it then needs to load WoodenToy4.png, it may not recognize it is the same as the woodentoy4.png that it already has in cache, and send a superfluous request for WoodenToy4.png.

To prevent this, set makeImageUrlsLowercase to true. This way, all images urls in the HTML and CSS sent to the browser will be lowercase, so there is no inconsistent casing. Note that CombineAndMinify doesn't change your source files. Instead, it changes the HTML that gets generated from your source files and sent to the browser.

insertVersionIdInImageUrls

Determines whether CombineAndMinify inserts version ids in image file names.

Value Description
true Version ids are inserted in image file names.
false
(default)
No version ids are inserted in image file names

Example

<configuration>
    ...
    <combineAndMinify insertVersionIdInImageUrls="true" >

    </combineAndMinify>
    ...
</configuration>

When a browser receives an image, it stores it in its browser cache. That way, if it needs to load the image again, it may still be in cache, so there is no need to send a request to the server. The result is less waiting for the visitor and less bandwidth used by your server.

One issue here is how long the browser should keep the image in its cache. Too long, and you may have visitors looking at outdated images. Too short, and the browser sends more requests for the image than necessary.

By setting insertVersionIdInImageUrls to true, you get the best of both worlds:

  • It causes CombineAndMinify to insert a version id into the image file name as used in image tags (both in the page HTML and in the CSS). CombineAndMinify calculates that version id from the last update time of the image file - so if you update an image, the version id changes. That way, when you update an image, the browser immediately picks up the new image. It won't pick up the image it has in cache, because that has a file name with the old version id.
  • Because of this, you can now tell the browser to cache images for up to a year - the maximum you can ask for according to the HTTP specification - without ever presenting outdated images to the visitor.

If you use IIS 7, you can tell the web server to cache all static files - images, CSS files, JavaScript files, etc. - for a year by adding a staticContent element to the system.webServer in your web.config:

<configuration>
    ...
    <system.webServer>
        ...
        <staticContent>
            <clientCache cacheControlCustom="Cache-Control: public"
                cacheControlMode="UseMaxAge"
                cacheControlMaxAge="365.00:00:00"/>
        </staticContent>
        ...
    </system.webServer>
    ...
</configuration>

Doing this for IIS 6 is a bit more complicated. Because most people will be using IIS 7 by now, I'll just refer to chapter 12 of my book ASP.NET Performance Secrets for instructions on how to set caching for static files in IIS 6.

A few more details about how this feature works:

  • There is no need to change your image file names manually. CombineAndMinify will create copies of the image files, with names that include the version ids (unless you changed this with the enableGeneratedFiles attribute). It also updates the page html on the fly before it is sent to the browser, so the browser will request those copies. Note that your actual source code or image file names are not changed.
  • To find out the version id, CombineAndMinify needs to access the file system to find out the last update time of the image file. You don't want this to happen for each request, so CombineAndMinify stores the version ids in server cache. These cache entries are invalidated the moment the underlying file changes, so the cache is never outdated.
  • Why insert the version id in the file name? Why not just add it as a query string? That would be a bit easier to handle. However, proxies (intermediate servers that pass on messages on the Internet) are less likely to cache files with query strings. So by inserting the version id in the file name instead of using a query string, you gain maximum proxy caching.
  • There is no counterpart of insertVersionIdInImageUrls for JavaScript and CSS files, because CombineAndMinify always uses version ids for those files.

For a lot more on browser caching and proxy caching, see www.iis.net or chapter 12 of my my recently released book ASP.NET Performance Secrets.

insertVersionIdInFontUrls

Determines whether CombineAndMinify inserts version ids in font file names loaded with the @font-face rule. If you don't use the @font-face rule in your CSS files, you can safely skip this attribute.

Value Description
true Version ids are inserted in font file names.
false
(default)
No version ids are inserted in font file names

Example

<configuration>
    ...
    <combineAndMinify insertVersionIdInFontUrls="true" >

    </combineAndMinify>
    ...
</configuration>

CSS allows you to load font files using the @font-face rule. insertVersionIdInFontUrls lets you insert version ids in the names of those files. It is to font files what insertVersionIdInImageUrls is to image files. See the description of insertVersionIdInImageUrls for the principle behind these two attributes.

exceptionOnMissingFile

Determines whether CombineAndMinify throws an exception when an image file is missing.

Value Description
Never
(default)
CombineAndMinify never throws an exception when an image file is missing.
Always CombineAndMinify always throws an exception when an image file is missing.
ReleaseModeOnly CombineAndMinify only throws an exception if the site is in release mode.
DebugModeOnly CombineAndMinify only throws an exception if the site is in debug mode.

Example

<configuration>
    ...
    <combineAndMinify exceptionOnMissingFile="DebugModeOnly" insertVersionIdInImageUrls="true" >

    </combineAndMinify>
    ...
</configuration>

Assume insertVersionIdInImageUrls is set to true, so CombineAndMinify inserts a version id in all image urls. This means it has to access each image file to find its last updated time. What happens if the image file cannot be found? That is determined by the exceptionOnMissingFile attribute:

  • If exceptionOnMissingFile is active (see table above) and CombineAndMinify finds that an image file cannot be found, it throws an exception with the path of the image. That makes it easier to find missing images.
  • If exceptionOnMissingFile is not active, CombineAndMinify doesn't throw an exception but recovers by not inserting a version id in the image url.

If all images should be present in your development environment, than it makes sense to set exceptionOnMissingFile to DebugModeOnly. That way, you quickly find broken images while developing your site, while preventing exceptions in your live site where you probably prefer a broken image over an exception.

What about JavaScript and CSS files? CombineAndMinify accesses these files when combining and / or minifying them:

  • If exceptionOnMissingFile is active and a JavaScript or CSS files can't be found, you'll get an exception, just as with images.
  • If exceptionOnMissingFile is not active and a JavaScript or CSS files can't be found, it just writes a comment in the combined and / or minified file, specifying the full name of the file that couldn't be found.

Keep in mind that if you want exceptions while the site is in debug mode, you have to ensure that CombineAndMinify is actually active in debug mode - set active to Always to make that happen.

removeWhitespace

Determines whether CombineAndMinify removes superfluous white space and comments from the HTML of the page.

Value Description
true Superfluous white space and comments are removed.
false
(default)
No superfluous white space and comments are removed.

Example

<configuration>
    ...
    <combineAndMinify removeWhitespace="true" >

    </combineAndMinify>
    ...
</configuration>

When you set removeWhitespace to true, CombineAndMinify removes all HTML comments from the page and collapses all runs of white space into a space. However, if a run of white space contains one or more line breaks, it is collapsed into a line break. That way, inline JavaScript will not be broken.

enableAxdProcessing

Determines whether CombineAndMinify processes .axd files. For more information about this feature, see the section Combining .axd files.

Value Description
true
(default)
.axd files are processed.
false .axd files are not processed.

Example

<configuration>
    ...
    <combineAndMinify enableAxdProcessing="true" >

    </combineAndMinify>
    ...
</configuration>

headCaching

Determines how tags for combined JavaScript files and CSS files are cached.

Value Description
None
(default)
Caching of replacement tags is switched off.
PerSite There is a single cache entry for the entire site.
PerFolder There is a cache entry per folder.
PerPage There is a cache entry per page (ignoring any query string).
PerUrl There is a cache entry per url (including any query string).

Example

<configuration>
    ...
    <combineAndMinify headCaching="PerSite" >

    </combineAndMinify>
    ...
</configuration>

Even though CombineAndMinify caches all minified and combined files, there is still some work involved in replacing tags of individual JavaScript files and CSS files with tags of combined files. Without further caching, this needs to be done for each page request.

To reduce the CPU usage involved in this, CombineAndMinify provides the option to cache the replacement tags. The recommended way to do this depends on the way you load your JavaScript and and CSS files:

Situation Recommended
Value
The additional CPU usage of CombineAndMinify is not an issue. Or the tags to load JavaScript and CSS files are totally ad hoc per page. None
(default)
All pages load the same JavaScript and CSS files in the same order. For example, all pages uses a single master page, and the master page has all the script and link tags to load the JavaScript and CSS files. PerSite
Your pages are split over folders, and the JavaScript and CSS files you load depend on the folder. For example, pages in the admin folder all load the same JavaScript and CSS files in the same order, but those files are different from the ones loaded by pages in the products folder. PerFolder
Each page loads different JavaScript and / or CSS files. However, the query string doesn't influence which files are loaded. So toys.aspx?id=1 and toys.aspx?id=2 load the same files in the same order, but categories.aspx loads different files. PerPage
The JavaScript and CSS files used by a page depend on the entire url, including its query string. So toys.aspx?id=1 and toys.aspx?id=2 load different files. PerUrl

The headCaching attribute really comes into its own if you load JavaScript and CSS files from a master page or a shared user control. This is because CombineAndMinify caches entire groups of tags, and the tags to replace those groups with. This process is sensitive to for example white space in between tags. That means that

<script type="text/javascript" src="/js/script1.js" ></script>
<script type="text/javascript" src="/js/script2.js" ></script>

and

<script type="text/javascript" src="/js/script1.js" ></script>

<script type="text/javascript" src="/js/script2.js" ></script>

are not the same, due to the extra white line in the second block.

generatedFolder

Determines the name of the folder into which generated files are stored.

Type Default
String ___generated

Example

<configuration>
    ...
    <combineAndMinify generatedFolder="anotherGeneratedFolder" >

    </combineAndMinify>
    ...
</configuration>

As you saw earlier on, unless you set generatedFolder to false, CombineAndMinify writes the processed versions of files (such as minified JavaScript files) to a separate folder. It also modifies the html that is being sent to the browser, so script tags, etc. pick up these generated files.

If this folder doesn't exist, it is created automatically by CombineAndMinify.

By default, the name of this folder is ___generated, and it lives in the root folder of your site. Normally, this name doesn't clash with anything. But if it does, the generatedFolder attribute allows you to specify another name.

enableGeneratedFiles

Determines whether processed versions of files are stored on disk or in memory.

Value Description
true
(default)
Processed versions of files are stored on disk.
false Processed versions of files are stored in memory.

Example

<configuration>
    ...
    <combineAndMinify enableGeneratedFiles="false" >

    </combineAndMinify>
    ...
</configuration>

CombineAndMinify processes files into new files - it minifies CSS and Javascript files and combines them, it inserts version ids in images file and font files, etc. Because doing all this processing for each request is inefficient, the processed versions of these files need to be stored somehow so they can be reused for many requests.

CombineAndMinify provides two ways to store processed files:

  • On disk, in a separate folder. This is the default. Not only is this a simple method, it also means that the processed files can be treated as simple static files. As a result, IIS will read these files into fast kernel cache - which is faster than the normal cache used by the second method below. Essentially, the advantage of this option is better performance.

    Additionally, cookieless domains can only be used when processed files are stored on disk.

  • In memory, in the ASP.NET cache. This is slower than the first method, and is more complicated because it means using an HTTP Handler to process all requests for CSS, JavaScript, image and font files, to read them from memory rather than disk. The advantage however is that no disk space is used for processed files.

    Note that Cassini, the web server built into Visual Studio, does not support HTTP Handers. To use CombineAndMinify with enableGeneratedFiles set to false while debugging, install IIS 7 on your development machine and get Vistual Studio to use IIS 7 instead of Cassini.

If you decided to store processed files in memory instead of on disk, set enableGeneratedFiles to false and follow the Additional Installation instructions below to configure the required HTTP Handler.

Additional Installation for IIS 7

If you use IIS 7, use the instructions in this section. Skip to the next section if you use IIS 6 or IIS 7 in classic mode.

To configure the HTTP Handler, add the following to your web.config:

</configuration>
    ...
    <system.webServer>
        <validation validateIntegratedModeConfiguration="false"/>
        ...
        <handlers>
            ...
            <add name="JavaScriptHandler" verb="*" path="*.js" type="CombineAndMinify.HttpHandler, CombineAndMinify" resourceType="Unspecified"/>
            <add name="CssHandler" verb="*" path="*.css" type="CombineAndMinify.HttpHandler, CombineAndMinify" resourceType="Unspecified"/>

            <!-- required if you use the insertVersionIdInImageUrls attribute -->
            <add name="GifHandler" verb="*" path="*.gif" type="CombineAndMinify.HttpHandler, CombineAndMinify" resourceType="Unspecified"/>
            <add name="PngHandler" verb="*" path="*.png" type="CombineAndMinify.HttpHandler, CombineAndMinify" resourceType="Unspecified"/>
            <add name="JpegHandler" verb="*" path="*.jpg" type="CombineAndMinify.HttpHandler, CombineAndMinify" resourceType="Unspecified"/>

            <!-- required if you use the insertVersionIdInFontUrls attribute -->
            <add name="WoffHandler" verb="*" path="*.woff" type="CombineAndMinify.HttpHandler, CombineAndMinify" resourceType="Unspecified"/>
            <add name="TtfHandler" verb="*" path="*.ttf" type="CombineAndMinify.HttpHandler, CombineAndMinify" resourceType="Unspecified"/>
            <add name="SvgHandler" verb="*" path="*.svg" type="CombineAndMinify.HttpHandler, CombineAndMinify" resourceType="Unspecified"/>
            <add name="EotHandler" verb="*" path="*.jpg" type="CombineAndMinify.HttpHandler, CombineAndMinify" resourceType="Unspecified"/>

        </handlers>
        ...
    </system.webServer>
    ...
</configuration>

Additional Installation for IIS 6

  1. Configure the HTTP Handler in your web.config:

    <configuration>
        ...
        <system.web>
            ...
            <httpHandlers>
                ...
                <add verb="*" path="*.js" type="CombineAndMinify.HttpHandler, CombineAndMinify" />
                <add verb="*" path="*.css" type="CombineAndMinify.HttpHandler, CombineAndMinify" />
    
                <!-- required if you use the insertVersionIdInImageUrls attribute -->
                <add verb="*" path="*.gif" type="CombineAndMinify.HttpHandler, CombineAndMinify" />
                <add verb="*" path="*.png" type="CombineAndMinify.HttpHandler, CombineAndMinify" />
                <add verb="*" path="*.jpg" type="CombineAndMinify.HttpHandler, CombineAndMinify" />
    
                <!-- required if you use the insertVersionIdInFontUrls attribute -->
                <add verb="*" path="*.woff" type="CombineAndMinify.HttpHandler, CombineAndMinify" />
                <add verb="*" path="*.ttf" type="CombineAndMinify.HttpHandler, CombineAndMinify" />
                <add verb="*" path="*.svg" type="CombineAndMinify.HttpHandler, CombineAndMinify" />
                <add verb="*" path="*.eot" type="CombineAndMinify.HttpHandler, CombineAndMinify" />
    
            </httpHandlers>
            ...
        </system.web>
        ...
    </configuration>
        
  2. Send all requests for JavaScript and CSS files (and optionally image and font files) to the ASP.NET handler. The ASP.NET handler will pass these on to the HTTP Handler (because of the lines you added to your web.config), allowing it to serve them from memory.

    1. Open the IIS Manager - click Start | Administrative Tools | Internet Information Services (IIS) Manager.
    2. Expand your server. Expand Web Sites. Right click your web site and choose Properties.
    3. Click the Home Directory tab, and then click the Configuration button.
    4. Get the path to the ASP.NET handler:
      1. Click the line with the .aspx extension.
      2. Click the Edit button.
      3. Copy the contents of the Executable field. For .Net 2 and .Net 3.5, it will be C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll. For .Net 4, it will be C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll.
      4. Click Cancel to dismiss the dialog.
    5. Tell IIS 6 to let ASP.NET handle all requests for files with extension .js:

      1. Click the Add button.
      2. Paste the path to the ASP.NET handler you just found into the Executable field.
      3. Enter .js into the Extension field.
      4. Uncheck the Verify that file exists box.
      5. Click OK.
    6. Repeat the last step, for extentions .css.
    7. If you set insertVersionIdInImageUrls to true, repeat the last step for these extensions:
      • .gif
      • .png
      • .jpg
    8. If you set insertVersionIdInFontUrls to true, repeat the last step for these extensions:
      • .woff
      • .ttf
      • .svg
      • .eot
    9. Click OK to dismiss the Configuration dialog.
    10. Click the Apply button, and then click OK to dismiss the Properties dialog.

Conclusion

The CombineAndMinify package will improve the performance of any web site that loads JavaScript, CSS, image or font files. Please give it a try with your site. If you find any bugs or have any trouble with the documentation, let me know and I'll try to fix the issue. Feature requests would be welcome too.

History

Version Released Description
1.0 6 Nov 2010 Initial release.
1.1 13 Nov 2010 Bug fixes. CombineAndMinify now correctly handles inlined images and image urls in CSS files surrounded by quotes.

CombineAndMinify can now be used in conjunction with Microsoft's Sprite and Image Optimization Framework. That framework combines several small images into one, reducing overall load times.

1.2 23 Jan 2011 Several bug fixes and enhancements, including:

If a JavaScript or CSS file contains non-ASCII characters (such as Chinese or Greek), it no longer uses the YUI minifier, because this mangles non-ASCII characters. Instead, it uses a port of JSMIN (class JsminCs in the sources). Note that for JavaScript or CSS files that only have ASCII characters (which would almost always be the case for sites in English), CombineAndMinify continues to use the more efficient YUI minifier.

CSS files with different media (such as 'screen', 'print', etc.) are now processed correctly (CombineAndMinify used to incorrectly combine CSS files with different media).

CombineAndMinify now processes script tags and link tags (for CSS files) with absolute URLs, as long as the URL points to a file on the site itself.

CombineAndMinify no longer attempts to process images generated by an HTTP Handler.

1.2.1 30 Jan 2011 Fixed a bug related to link tags that have a media property and also load an external stylesheet. Also fixed a bug that affected CSS files that both contain non-ASCII characters and use external background images.
1.3 12 Feb 2011 Added support for the @font-face rule. Added the insertVersionIdInFontUrls web.config attribute.
1.3.1 19 Mar 2011 Minor bug fix to prevent the page crashing if it contains a LiteralControl with null Text.
1.3.2 10 May 2011 When minifying a CSS file containing non-English characters, too many spaces were removed thereby breaking descendant selectors. This has now been fixed.
1.4 15 May 2011 CombineAndMinify now processes .axd files. This will improve page load times when you use ASP.NET AJAX toolkit controls.
1.4.1 22 May 2011 Two related bug fixes:

1) Script and link tags commented out with <!-- --> are no longer processed.

2) Conditional incudes of the form

<!--[if IE 7]>
<link href="css/ie7Fixes.css" rel="stylesheet" type="text/css" />
<![endif]-->
will no longer be combined with other script or css files. Note that conditional includes do not get processed at all by CombineAndMinify (they get ignored as comments), so there is no minification, etc.
1.4.2 16 June 2011 CombineAndMinify no longer crashes when confronted with a static file with a query string, such as "script1.js?fp789". It now ignores any query string on static files, because query strings normally do not change the contents of these files. Note that this does not include .axd files, where the query string does determine the content of the file.
1.5 1 July 2011 CombineAndMinify now sends ETags with generated content.
1.6 6 July 2011 CombineAndMinify can now run with trust level medium. This means it can now be used on most (if not all) shared hosting plans that support ASP.NET.

If you are upgrading from an earlier version:

1) You need to modify your web.config, to include requirePermission="false" in the <section> definition:

<section name="combineAndMinify" type="CombineAndMinify.ConfigSection" requirePermission="false" /> 

2) This version uses versions of the Yahoo.Yui.Compressor.dll and EcmaScript.NET.modified.dll binaries that can run with medium trust, so you need to replace those as well along with the CombineAndMinify.dll (you'll find them in the bin directory of the CombineAndMinify project).

3) If you use a shared hosting plan and controls from the ASP.NET AJAX toolkit, also read section Additional configuration when using a shared hosting plan.

1.7 22 Sep 2011 Protocol relative urls are now handled correctly. Code generated for preloadAllImages now wrapped in an anonymous function to reduce pollution of the name space.
1.8 11 Dec 2011 img tags within Repeaters with data bound image urls are now processed.
2.0 12 Mar 2012 By default, CombineAndMinify now writes combined files to disk, instead of keeping them in cache. Cookieless domains now work properly. Installation of CombineAndMinify has been greatly simplified.
2.1 29 Mar 2012 Fixed a bug kicking in when headCaching="None".

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here