Introduction
Using compression is the single most
effective way to reduce page load times. The .aspx files
sent by the server to the browser consist of HTML. HTML is highly compressible
by algorithms such as gzip. Because of this, modern web servers including IIS 5
and later have the ability to compress outgoing files, and modern browsers have
the ability to decompress incoming files.
Both IIS 6 and IIS 7 offer advanced compression related options
that help you get better performance improvements for your web site and make better use of your
servers and bandwidth. Unfortunately, these options are not always easy to access. This article series
shows step by step how to unlock these options.
In the first article in this 2 part series, we'll focus on configuring IIS 7 compression.
If you are used to IIS 6, you'll find that IIS 7 offers many new features, including the ability to
cache not only compressed static files, but also compressed dynamic files. If you still use IIS 6, the next article in the series
will show how to configure IIS 6 compression.
Contents
Request and response headers involved in compression
How does the server know that the browser
can accept compressed content? And how does the browser know that the content it
received is compressed?
When a browser that supports compression
sends a request to the server, it includes the request header
Accept-Encoding telling the server which compression algorithms it supports. For
example:
Accept-Encoding: gzip,deflate
If the server then uses compression for
its response, it includes the response header Content-Encoding in the (uncompressed) file header to say how the file has been
compressed, as shown:
Content-Encoding: gzip
This keeps the browser and server compression-wise
in sync. However, it isn't only browsers and servers
that send and receive requests and responses, but proxies as well. And proxies
can cache responses and serve subsequent requests from their cache. When a
proxy caches a compressed file, how do we make sure that the proxy doesn't send
that compressed file to a browser that can't process compressed files?
The solution adopted by IIS 6 and IIS 7
is to tell the proxy that if it receives a request without the Accept-Encoding request header, it must not serve a file that was sent in response
to a request that did have the Accept-Encoding request header, or vice versa,
that is, the Accept-Encoding request
headers must match. IIS 6 and 7 make this happen by sending a Vary header in the response from the server when compression is enabled as
shown:
Vary: Accept-Encoding
IIS 6 also lets you override the Cache-Control and
Expires headers for
compressed files via properties in its metabase. This allows you to suppress
proxy caching for compressed files. The IIS 6 metabase will be described in part 2, about configuring IIS 6 compression.
The metabase properties that override the Cache-Control and
Expires headers can be
found
here
Starting configuration of IIS 7 compression
Before you start
configuration of IIS 7 compression,
find out at the following sites whether your pages already use compression, and if so, how much bandwidth you're saving:
Enabling compression in IIS 7 essentially consists of these steps:
- Installing the dynamic content compression module;
- Enabling Compression;
- Configuring advanced features.
Lets go through these steps one by one.
Installing the dynamic content compression module
If you want to use compression for dynamic files, first install the
dynamic content compression module. The steps to do this are different
depending on whether you use Vista/Windows 7 or Windows Server 2008.
On Windows Server 2008:
- Click Start |
Administrative Tools | Server Manager.
- On the left-hand side, expand Roles and then click on Web Server (IIS).
- Scroll down to the Role Services
section and then click on Add Role Services. The Add Role Services wizard opens:
- On the Select
Role Services
page, scroll down to the Performance section
and select Dynamic Content Compression.
Click on Next.
- Read the message and click Install.
- Once the installation is done, close the wizard.
On Vista or Windows 7:
- Click on Start
| Control Panel
| Programs | Turn
Windows features
on or off. The Windows
Features dialog opens.
- Expand Internet
Information Services,
expand World Wide
Web Services,
and expand Performance Features. Select Http
Compression Dynamic.
- Click on OK.
Wait for the feature to be configured.
Enabling compression
Now enable compression in the IIS manager:
- Open IIS manager. Click on Start | Control Panel. Type admin
in the search box. Click on Administrative Tools. Double-click on Internet Information
Services (IIS)
Manager.
- Click on your machine. Then double-click on the Compression icon on the right-hand side.
- The compression window opens. Here you can
enable compression for dynamic content and static content. The window shows the
following items:
dynamic content compression: Unless your server already uses a lot of CPU, you will want to
enable dynamic content compression.
Enable static content compression: You can safely enable static content compression because
compressed static content gets cached. So, only the initial compression takes
CPU cycles.
Only compress files larger than (in bytes):
It makes sense to not compress small files. Because compression produces some
overhead in the file, compressing a small file may actually make it bigger
rather than smaller.
Cache directory:
This is where compressed static files are stored. If you are short on disk
space on the system drive, consider putting this on another drive. Make sure that
the drive is a local drive or NTFS partition, and that it isn't compressed or
shared.
Per application pool disk space limit (in MB): If
you have lots of application pools and limited disk space, you may want to
adjust this. If you have 100 application pools and you leave this at 100MB, 100
x 100MB = 10GB may be used to cache static compressed files.
On the right-hand side of the window, click on Apply. Compression is now enabled.
Setting compression by site, folder, or file
In addition to enabling or disabling
compression for all sites on the server, you can enable or disable compression
at a site level, or even a folder or file level.
To make this work:
- Open the IIS Manager and in the left-hand side
click on the site, folder, or file whose compression status you want to change.
- Make sure that the middle pane is switched to Features View,
and double-click on the Compression icon.
- This will open a window where you can enable or
disable compression for dynamic or static files:
Compression level
You can tweak the tradeoff between compression and CPU usage by
setting the compression level. The higher the compression level, the greater the
compression and CPU usage.
The compression level can be set
separately for static and dynamic files. For static files, use 9, the highest level.
For dynamic files, compression level 4 seems to be the sweet spot, as shown in
this study
However, the optimal compression level for
your website may be different, depending on how much spare CPU capacity you
have, the compressibility of your pages, and your bandwidth costs. Experiment
with different levels to see which one works best for you.
To set the compression level:
- Execute this from the command prompt:
C:\Windows\System32\Inetsrv\Appcmd.exe
set config -section:httpCompression
-[name='gzip'].staticCompressionLevel:9
-[name='gzip'].dynamicCompressionLevel:4
(This sets compression level 9 for static files and
compression level 4 for dynamic files).
- Reset the IIS server to make the new compression
level take effect. In IIS Manager, click on the server at the top of the tree
and then click on Restart on the right-hand
side.
Disabling compression based on CPU usage
To make sure that compression doesn't overload the CPU, IIS 7
calculates average CPU usage every 30 seconds. It automatically switches off
compression when CPU usage exceeds a given limit. Then when CPU usage drops
below a second limit, it switches on compression again.
The default values for these limits are:
|
Switch compression off at (CPU usage)
|
Switch back on at
(CPU usage)
|
Dynamic files
|
90 percent
|
50 percent
|
Static files
|
100 percent
|
50 percent
|
Note that this means that if CPU usage on
your server is consistently over 50 percent, and when it spikes over 90 percent,
compression for dynamic files will be switched off, but will never be switched
back on again.
You can change these limits by modifying the applicationHost.config file,
which is normally in folder C:\Windows\System32\inetsrv\config:
- Make a backup copy of applicationHost.config.
- Open applicationHost.config with a text editor.
- Find the <httpCompression> section.
- To change the CPU usage at which compression for
dynamic files is switched back on to 70 percent, add the dynamicCompressionEnableCpuUsage
attribute to the httpCompression element, as shown:
<httpCompression dynamicCompressionEnableCpuUsage="70" .... >
Note that you provide a number to the attribute, not a percentage,
so don't write a percentage sign when setting the attribute.
The value 70 shown here is simply an example, not a recommendation. You need to
determine the optimal value for your own site.
- Save the applicationHost.config file.
- Reset the IIS server to make the new compression
level take effect. Start IIS Manager, click on the server at the top of the
tree, and then click on Restart on the right-hand
side.
In case you want to change any of the
other limits, here are the matching attributes:
|
Switch compression off at
(CPU usage)
|
Switch back on at
(CPU usage)
|
Dynamic files
|
dynamicCompressionDisableCpuUsage
|
dynamicCompressionEnableCpuUsage
|
Static files
|
staticCompressionDisableCpuUsage
|
staticCompressionEnableCpuUsage
|
If you want to stop IIS from ever
switching off compression based on CPU usage, set all these attributes to 100.
You will find all the elements and
attributes that can be used with httpCompression
here
Setting the request frequency threshold for static compression
As you saw earlier, IIS 7 caches the
compressed versions of static files. So, if a request arrives for a static file
whose compressed version is already in the cache, it doesn’t need to be
compressed again.
But what if there is no compressed
version in the cache? Will IIS 7 then compress the file right away and put it
in the cache? The answer is yes, but only if the file is being requested
frequently. By not compressing files that are only requested infrequently, IIS
7 saves CPU usage and cache space.
By default, a file is considered to be
requested frequently if it is requested two or more times per 10 seconds.
This is determined by two attributes in the serverRuntime
Element in web.config:
serverRuntime attribute
|
Description
|
frequentHitThreshold
|
Number of times a URL must be
requested within the time span specified in the frequentHitTimePeriod
attribute to be considered frequently hit.
Must be between 1 and 2147483647. Default is
2 .
|
frequentHitTimePeriod
|
Time interval in which a URL must
be requested the number of times specified in the frequentHitThreshold
attribute before it is considered to be frequently hit. Default
is 10 seconds.
|
This means that when a static file is
requested for the very first time, it won’t be compressed.
For example, to specify that static files
need to be hit seven times per 15 seconds before they will be compressed, use:
<configuration>
...
<system.webServer>
<serverRuntime frequentHitThreshold="7" frequentHitTimePeriod="00:00:15" />
</system.webServer>
...
</configuration>
Caching compressed dynamic files
You've seen that IIS 7 caches only the compressed version of static
files, and that dynamic files are compressed for each request (provided that
dynamic file compression is enabled). This means that compressing dynamic files
takes much more CPU than static files.
That makes sense if the dynamic files are different for each
visitor, for example if each page contains personal information. However, if the
dynamic pages are fairly static and the same for all visitors, it makes sense
to cache their compressed versions too.
You may already use the ASP.NET
OutputCache
directive to cache your .aspx pages. The issue is that by default,
IIS stores the uncompressed version of the file in the output cache, rather than the compressed version. For each
request, IIS then has to compress the contents of the cache before sending it
to the browser. This is not very efficient.
Storing compressed files in the output cache
Here is how to get IIS to cache the compressed version of the file,
rather than the uncompressed version. That way, it doesn't have to compress the
file for each request, reducing CPU usage.
Because this uses ASP.NET output caching,
you need to use the OutputCache directive in
your pages, as shown:
<%@ OutputCache Duration="300" VaryByParam="none" %>
This caches the page for 300 seconds.
Now to get IIS to cache the compressed
version rather than the uncompressed version, modify the applicationHost.config file.
You'll normally find this file in folder C:\Windows\System32\inetsrv\config:
- Make a backup copy of applicationHost.config.
- Open applicationHost.config with a text editor.
- Find the <urlCompression> section.
- Add the dynamicCompressionBeforeCache="true" attribute to the urlCompression element, as shown:
<urlCompression dynamicCompressionBeforeCache="true" ... />
- Save the applicationHost.config file.
- Reset the IIS server to make the new attribute
take effect.
- Start IIS Manager, click the server at the top
of the tree, and then click Restart on the right-hand
side.
What if a client doesn't accept compressed content?
Now that we're caching compressed
content, what happens if someone visits your site with a browser that doesn't
accept compressed content?
To simulate this eventuality, let's send
a request to the server that doesn't have the
Accept-Encoding
request header. This should force the server to send uncompressed
content.
To do this, we'll use Fiddler, a free
proxy which allows you to "fiddle" with requests and responses while
they are travelling between browser and server. It's easiest to do this with
Firefox:
- Open Firefox and download Fiddler at http://www.fiddler2.com/fiddler2/
- Install Fiddler and start it.
- On the Firefox status bar, switch on forcing
traffic to Fiddler.
- At this stage, when you visit a page with
Firefox, the server should still use compression
(assuming the request has an Accept-Encoding request header allowing gzip compression).
You can measure the actual size of your compressed pages as they will travel over the Internet to the browser,
using the Web Developer add on for Firefox:
- Using Firefox, visit
http://chrispederick.com/work/web-developer
to download and install the Web Developer add on.
- After you have installed Web Developer, load the page again.
- Right click anywhere in the page. A popup menu will appear. Click Web Developer | Information | View Document Size.
A new window appears showing the groups of files making up the page.
- Expand the Documents group to see the size of the page.
If it was compressed while travelling over the Internet, you will also see its compressed size.
- Now get Fiddler to strip off the Accept-Encoding request header from the request going from Firefox to the web
server. In the Fiddler window on the right-hand side, click on the Filters tab, select Use Filters, select
Delete request header, and type in Accept-Encoding.
- Refresh the page in Firefox. Check the file size
again with Web Developer. You should find that no compression was used with
this request. That will make browsers that do not support compression happy. So
far, so good.
- In Fiddler, uncheck the Delete request header checkbox.
As a result, the Accept-Encoding request header now makes it to the web server again.
- Refresh the page. The server should now compress
the file again. But if you check with Web Developer, you'll find that it is
still sending files uncompressed!
This is because when IIS received the request for uncompressed
content, it threw away the compressed contents in the cache, regenerated the
content and stored it uncompressed in the cache. It then keeps serving this
uncompressed content until the cache expires, even to clients that accept
compressed content.
- You can prevent this from happening by caching
both compressed and uncompressed content. You do that by including VaryByContentEncoding in the
OutputCache directive, as
shown in the following code:
<%@ OutputCache Duration="300" VaryByParam="none" VaryByContentEncoding="gzip;deflate" %>
-
If you now delete the Accept-Encoding header and then let it go through again, you'll see that the server
always sends compressed content to clients that accept it, even if another
client didn't accept it.
-
Before you end this experiment and close
Fiddler, go back to the Firefox status bar and stop sending traffic to Fiddler.
Otherwise Firefox will complain about the missing proxy when you close Fiddler.
A drawback of using VaryByContentEncoding in the OutputCache directive is
that it disables
kernel caching for this file.
Kernel caching is a highly efficient form of server side caching, and is discussed in Chapter 5 of
my book
ASP.NET Site Performance Secrets.
So should you use VaryByContentEncoding in the OutputCache directive?
Seeing that you are reading this chapter, the gain in compression by using VaryByContentEncoding may well outweigh the loss of kernel caching, especially seeing
that you already use output caching. Your best bet would be to try both
scenarios in production for a while, and compare CPU usage, response times, and
bandwidth used per request for each scenario.
Improving the compressibility of your pages
If your server uses compression, then it
makes sense to optimize compressibility of your .aspx, JavaScript and CSS files. Compression
algorithms like repeating content, which puts a premium on consistency:
Instead of:
<a runat="server"......>
Likewise, within CSS selectors, write your
properties in alphabetical order.
Use consistent casing. Use all lowercase for HTML tags and attributes and you'll be
XHTML compliant as well.
Use consistent quoting: Don't mix "...." and
'....'.
Summary
In this article, we saw how to configure compression on IIS 7, including its more advanced, lesser known features.
In the next article, I'll show how to get the most out of the compression features built into IIS 6.
If you enjoy this series and want to know the full story on how to improve ASP.NET site performance, from database server to web server to browser,
consider my book
ASP.NET Site Performance Secrets.
Or visit my web site
ASP.NET Performance.