Enable built-in IIS compression in Azure

The time it takes to transfer an HTTP request and response across the network can be significantly reduced by decisions made by front-end engineers. It's true that the end-user's bandwidth speed, Internet service provider, proximity to peering exchange points, etc. are beyond the control of the development team. But there are other variables that affect response times. Compression reduces response times by reducing the size of the HTTP response. I found a bit tricky and interesting to enable compression in Azure service deployment.

IIS 7 improves internal compression functionality dramatically making it much easier than previous versions to take advantage of compression that’s built-in to the Web server. IIS 7 also supports dynamic compression which allows automatic compression of content created in your own applications (ASP.NET or otherwise!). The scheme is based on content-type sniffing and so it works with any kind of Web application framework.

While static compression on IIS 7 is super easy to set up and turned on by default for most text content (text/*, which includes HTML and CSS, as well as for JavaScript, Atom, XAML, XML), setting up dynamic compression is a bit more involved, mostly because the various default compression settings are set in multiple places down the IIS –> ASP.NET hierarchy.
There are two approaches for doing this: Static Compression and Dynamic Compression. Compression in IIS 7.x is configured with two .config file elements in the space. The elements can be set anywhere in the IIS/ASP.NET configuration pipeline all the way from ApplicationHost.config down to the local web.config file. However, the web.config approach is something that you may want to use on the standard machines and not Azure VMs. To make it work on Windows Azure  you should use ApplicationHost.config (in the %windir%\System32\inetsrv\config folder) on IIS 7.5 with a couple of small adjustments (added JSON output and enabled dynamic compression).

<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">

      <scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" staticCompressionLevel="9" />

      <scheme name="deflate" dll="%Windir%\system32\inetsrv\gzip.dll" staticCompressionLevel="9" />


        <add mimeType="application/atom+xml" enabled="true" />

        <add mimetype="application/json" enabled="true" />

        <add mimetype="application/xml" enabled="true" />

        <add mimetype="application/atom+xml;charset=utf-8" enabled="true" />



    <urlCompression doDynamicCompression="true" />

Now, it might be tempting to add an <httpCompression> section to your web.config, and add application/json to that. But that's just a bad way to waste a good hour or two - you can only change the <httpCompression> element at the applicationHost.config level in Azure startup task.” Here is the startup task to enable all necessary formats like Atom/xml, XML and JSON, pick which you need/like the most. You will need to add cmd startup task in elevated mode to your Azure ServiceDefinition.csdef config file. Elevated mode is used to give AppCmd.exe sufficient permissions to change the settings in the Web.config file: This example adds a compression section and a compression entry for JSON to the config file, with error handling and logging.

<Startup      <Task commandLine="EnableCompression.cmd" executionContext="elevated" taskType="simple"></Task> 
EnableCompression.cmd file can look like this:
REM   *** Add a compression section to the Web.config file. ***
%windir%\system32\inetsrv\appcmd set config /section:urlCompression /doDynamicCompression:True /commit:apphost >> "%TEMP%\StartupLog.txt" 2>&1
REM   ERRORLEVEL 183 occurs when trying to add a section that already exists. This error is expected if this
REM   batch file were executed twice. This can occur and must be accounted for in a Windows Azure startup
REM   task. To handle this situation, set the ERRORLEVEL to zero by using the Verify command. The Verify
REM   command will safely set the ERRORLEVEL to zero.
REM   If the ERRORLEVEL is not zero at this point, some other error occurred.
   ECHO Error adding a compression section to the Web.config file. >> "%TEMP%\StartupLog.txt" 2>&1
   GOTO ErrorExit
REM   *** Add compression for json. ***
%windir%\system32\inetsrv\appcmd set config  -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json; charset=utf-8',enabled='True']" /commit:apphost >> "%TEMP%\StartupLog.txt" 2>&1
   ECHO Error adding the JSON compression type to the Web.config file. >> "%TEMP%\StartupLog.txt" 2>&1
   GOTO ErrorExit
REM   *** Add compression for atom xml. ***
%windir%\system32\inetsrv\appcmd set config  -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/atom+xml; charset=utf-8',enabled='True']" /commit:apphost >> "%TEMP%\StartupLog.txt" 2>&1
   ECHO Error adding the atom compression type to the Web.config file. >> "%TEMP%\StartupLog.txt" 2>&1
   GOTO ErrorExit
REM   *** Add compression for XML. ***
%windir%\system32\inetsrv\appcmd set config  -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/xml; charset=utf-8',enabled='True']" /commit:apphost >> "%TEMP%\StartupLog.txt" 2>&1
   ECHO Error adding the XML compression type to the Web.config file. >> "%TEMP%\StartupLog.txt" 2>&1
   GOTO ErrorExit
REM   *** Exit batch file. ***
EXIT /b 0
REM   *** Log error and exit ***
REM   Report the date, time, and ERRORLEVEL of the error.
DATE /T >> "%TEMP%\StartupLog.txt" 2>&1
TIME /T >> "%TEMP%\StartupLog.txt" 2>&1
ECHO An error occurred during startup. ERRORLEVEL = %ERRORLEVEL% >> "%TEMP%\StartupLog.txt" 2>&1

Script uses AppCmd.exe command line tool can be used to manage IIS settings at startup on Windows Azure.AppCmd.exe provides convenient, command line access to configuration settings for use in startup tasks on Windows Azure. Using AppCmd.exe, Website settings can be added, modified, or removed for applications and sites. For more information on AppCmd.exe, see AppCmd.exe on Microsoft Technet. To use AppCmd.exe at startup, add the appropriate AppCmd.exe commands to your startup task. However, a few factors that can combine to complicate the use of AppCmd.exe in startup tasks: Startup tasks can be run more than once between reboots. This can happen if the role recycles, for instance. Some AppCmd.exe actions can generate errors if they are performed more than once. Attempting to add a section to Web.config twice could generate an error. Startup tasks fail if they return a non-zero exit code or errorlevel. This can happen if AppCmd.exe generates an error.

If you use latest updates from Azure OS, check if you are hitting exception System.IO.IOException: An exception has been thrown when reading the stream. ---> System.InvalidOperationException: Text cannot be written outside the root element.
at System.Xml.XmlBaseWriter.WriteBase64(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.Stream.InternalCopyTo(Stream destination, Int32 bufferSize)

Also please check this article: http://stackoverflow.com/questions/28068944/breaking-change-in-class-xmlwriterbackedstream-with-kb2901983 and if you have issue or not, but you can fix it with the following code in CompressionStreamBodyWriter below.

protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
            writer.WriteStartElement("Binary", string.Empty);
            writer.WriteBase64(new byte[0], 0, 0);


Gzipping generally reduces the response size by about 70%. Approximately 90% of today's Internet traffic travels through browsers that claim to support gzip. Gzipping as many file types as possible is an easy way to reduce page weight and accelerate the user experience. It looks to me that gzip option is better than deflate, because it is the same approach, but it supports more browsers: http://www.vervestudios.co/projects/compression-tests/results. When you are done with IIS compression you can celebrate Jean-Claude Van Damme’s Birthday (19 October):

Add comment