Using Compression for Scripts

Using Compression for Scripts

Modern browsers have the ability to decompress files, so you can reduce the time needed to download files to the browser by sending them in a compressed format. Text files compress very well. For example, the MicrosoftAjax.js file is about 85KB originally, but when compressed, it is reduced to about 25KB. There is some extra CPU usage on the server to do the compression, but the compressed scripts can be cached on the server, so the processing overhead shouldn’t matter much. The client will decompress the file once and then cache it as well so the impact there is minimal and the overall effect can be a benefit to the server and to the client.

Compression of Dynamic Scripts

Scripts embedded as a resource within a dll and extracted by ASP.NET on demand are referred to as dynamic scripts. ASP.NET AJAX supports compressing those scripts as they are extracted from the dll. Custom controls that use the ScriptManager to access their dynamic scripts will also get the benefit of having their JavaScript compressed and cached.

The compression of dynamic scripts is controlled in the web.config file. The enableCompression and enableCaching attributes should be set to true:

<system.web.extensions>
  <scripting>
    <scriptResourceHandler enableCompression="true" enableCaching="true" />
  </scripting>
</system.web.extensions> 

Note that there are some tradeoffs in using compression and caching. Using compression without caching will make the server perform the compression work for every request. This can increase the load significantly on the server. However, caching the scripts can increase the memory used by the ASP.NET worker process, putting pressure on the cache and affecting the overall throughput as a result.

On the other hand, the compressed scripts are smaller and thus transmitted more quickly to the browser, freeing up a connection and thread more quickly. Most environments will benefit from allowing the Script Resource Handler to compress and cache scripts, but busy servers need to be monitored to watch out for possible negative impacts to memory or processor usage.

Compression of Static Scripts

IIS can compress static scripts to get the same benefit. In IIS7, JavaScript files are compressed by default. In IIS6, this is not the default. Instead, it has to be enabled, and there are several steps necessary to set it up.

First, use the Internet Services Manager to go to the property pages for the web site. On the services tab, check the Compress static files check box, and specify a directory for the server to use in caching the compressed output. This saves the overhead of recompressing the content after a process restart.

The MMC snap-in does not expose one key setting that should be changed to enable compression of the .js files for both standard compression types that are supported. You have to make the change in the IIS6 metabase directly. You can use the adsutil.vbs file installed in \WINNT\System32\Inetpub\AdminScripts.

To enable IIS to use deflate and gzip compression on .js files, use the following command lines:

cscript.exe adsutil.vbs set w3svc/Filters/Compression/DEFLATE/HcFileExtensions "js"
cscript.exe adsutil.vbs set w3svc/Filters/Compression/GZIP/HcFileExtensions "js"

Now, IIS will compress static JavaScript files and cache them to disk. However, there is still one other change needed for IIS6. To avoid getting compressed scripts cached by proxy servers and gateways and returned to browsers that don’t support compression, the cache expiration header is set to a date in the past. When IIS6 was released, the level of support for compression in browsers was still lacking, but now it’s safer to assume that the browsers accessing a site have such support. If you leave a backdated expiration date, the browser will request the AJAX scripts on every page access to an AJAX-enabled page. I’d rather send 100K of uncompressed script to the browser once and have it cached than send 25K of compressed script on every page request. You can view the parameters being used for compression using the adsutil script and then modify them to a date you are comfortable with by using a set parameter instead of enum:

cscript.exe adsutil.vbs enum w3svc/filters/compression/parameters

You should look at the output and consider your application variables before setting a new expiration to ensure that you are picking an appropriate expiration for your environment.

Consider Using Shared Scripts

High-volume applications make use of edge servers to maximize the use of their hardware. They move lots of static content to a separate server in order to partition the load. The web server can deliver a lot of static content very fast and with low CPU impact. Multiple applications that all make use of the same set of resources gain an advantage when you coalesce them to a single place referenced by all applications. This also applies to a lot of ASP.NET AJAX applications on the same server, all serving up the same set of JavaScript files.

The ScriptManager control has a ScriptPath property that can be set to an absolute path to make use of a copy of the scripts shared by multiple applications and directories. This can avoid the impact of many different applications consuming additional memory to cache the scripts and bypasses the need to compress them again.

The ScriptManager uses the ScriptPath as the root location to search for scripts and takes into account the assembly name and version. If want to cache the scripts in a folder under the root of the web site, you thus need to copy them from their default location under C:\Program Files\Microsoft ASP.NET. For example, you could set the ScriptPath="/scripts" and then copy the files into C:\InetPub\wwwroot\scripts\System.Web.Extensions\1.0.61025.0\, assuming your web root folder is C:\InetPub\wwwroot.

Don’t Change Version Paths

The ScriptPath setting of the ScriptManager applies to all of the ScriptReferences that it contains. However, the ScriptReference has a Path property that can override that setting. You may be tempted to override the path directly to put the AJAX scripts in a simpler path without the version, but I recommend against it. The use of the version number identifies the scripts so that servicing and subsequent releases will use a different folder to avoid problems.

If you switch from the dynamic scripts to the static versions, you also need to be aware of a servicing concern. If the ASP.NET team releases an updated version of the scripts, you will need to update your deployed copy in order to get the fix. It will no longer be sufficient to just install the updated System.Web.Extensions.dll in the GAC.

Tip 

If you install a new version of System.Web.Extensions.dll in the GAC and create a new version-coded folder to hold the scripts, you may also want to keep the old dll in the GAC and keep the old scripts in place to service older applications that haven’t been updated yet.