Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

More on GZip compression with ASP.NET Content


:P
On this page:

After I posted the GZip Script Compression module code a while back, I’ve gotten a number of questions regarding GZip and compression in ASP.NET applications so I thought I show a couple of other ways you can use the new GZipStream class in ASP.NET.


The beauty of this new GZip support is how easy it is to use in your own ASP.NET code. I use the GZip functionality a bit in my WebLog code. For example the the cached RSS feed is GZip encoded which uses GZipStream on the Response.OutputStream fed to an XmlWriter():

 

Response.ContentType = "text/xml";

 

Stream Output = Response.OutputStream;

 

if (Westwind.Tools.wwWebUtils.IsGZipSupported())

{

    GZipStream gzip = new GZipStream(Response.OutputStream, CompressionMode.Compress);

    Response.AppendHeader("Content-Encoding", "gzip");

    Output = gzip;

}

 

Encoding Utf8 = new UTF8Encoding(false);  // No BOM!

XmlTextWriter Writer = new XmlTextWriter(Output,Utf8);

 

Writer.Formatting = Formatting.Indented;

 

Writer.WriteStartElement("rss");

 

 

Writer.WriteEndElement(); // rss

 

Writer.Close();

Response.End();

 

 

GZipStream is a stream acts like a stream filter so you can assign it to an existing stream like ResponseStream can intercept all inbound data then write the updated data into the actual Response stream. It’s super easy to do.

 

You should always check however if GZip is enabled which is done by this helper method IsGZipSupported() which looks like this:

 

/// <summary>

/// Determines if GZip is supported

/// </summary>

/// <returns></returns>

public static bool IsGZipSupported()

{

    string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];

    if (!string.IsNullOrEmpty(AcceptEncoding) &&

         AcceptEncoding.Contains("gzip") || AcceptEncoding.Contains("deflate") )

        return true;

    return false;

}

 

This ensures that you don’t encode content when the client doesn’t understand GZip and would be unable to read the encoded GZip content. Note that the code checks for either gzip or deflate so this assumes before encoding you’ll pick the right encoding algorithm.

GZip in Page Content

Speaking of encoding -  above was XML and raw ResponseStream encoding, but you can also apply GZip content very, very easily to your page level or any ASP.NET level code (such as in an HTTP handler). In fact, you can use a very generic mechanism to encode any output by using a Response.Filter which the following helper method (also in wwWebUtils) demonstrates:

 

/// <summary>
/// Sets up the current page or handler to use GZip through a Response.Filter
/// IMPORTANT:  
/// You have to call this method before any output is generated!
/// </summary>
public static void GZipEncodePage()
{
    HttpResponse Response = HttpContext.Current.Response;

    if (IsGZipSupported())
    {
        string AcceptEncoding = HttpContext.Current.Request.Headers["Accept-Encoding"];
        if (AcceptEncoding.Contains("deflate"))
        {
            Response.Filter = new System.IO.Compression.DeflateStream(Response.Filter,
                                       System.IO.Compression.CompressionMode.Compress);
            Response.AppendHeader("Content-Encoding", "deflate");
        }
        else
        {
            Response.Filter = new System.IO.Compression.GZipStream(Response.Filter,
                                      System.IO.Compression.CompressionMode.Compress);
            Response.AppendHeader("Content-Encoding", "gzip");                    
        }
    }

    // Allow proxy servers to cache encoded and unencoded versions separately
    Response.AppendHeader("Vary", "Content-Encoding");
}

 

You can now take this helper function and use this in page level code. For example, the main page in my blog which is HUGE (frequently around 500k) uses it like this:

 

protected void Page_Load(object sender, EventArgs e)

{

    wwWebUtils.GZipEncodePage();

 

    Entry = WebLogFactory.GetEntry();

 

    if (Entry.GetLastEntries(App.Configuration.ShowEntryCount, "pk,Title,Body,Entered,Feedback,Location") < 0)

        throw new ApplicationException("Couldn't load WebLog Entries: " + Entry.ErrorMessage);

 

    this.repEntries.DataSource = this.Entry.EntryList;

    this.repEntries.DataBind();

}

 

That’s it. One line and the page will now conditionally GZip encode if the client supports it.

 

If you really wanted to you can take this even one step further and create a module that automatically sets the Response.Filter early in the pipeline and based on that automatically compresses all content.

 

However, remember GZip encoding applies compression on the fly so there’s some overhead in the GZip encoding – you are basically adding more CPU processing to your content to reduce the output size. If your content is not that large to start with there’s probably not that much sense in compressing in the first place so I don’t think that whole-sale compression of dynamic content is a good idea.

 

Caching

But one thing that can mitigate the overhead of GZip compression is caching.

 

Ah yes, both the RSS feed and the home page are usually heavily cached – the content doesn’t change all that frequently so there’s no reason to keep re-generating it right? If you use GZip on your content you have to be careful to cache both the GZipped content and the non-encoded content or else you’ll feed garbage to clients that don’t understand GZip.

 

So for example the default page has:

 

<%@ OutputCache Duration="60" VaryByParam="none" VaryByCustom="GZIP" %>

 

And then a custom Global.asax handler that looks like this:

 

public override string GetVaryByCustomString(HttpContext context, string custom)

{

    if (custom == "GZIP")

    {

        if (Westwind.Tools.wwWebUtils.IsGZipSupported())

            return "GZip";

        return "";

    }

 

    return base.GetVaryByCustomString(context, custom);

}

 

Which results in possibly two different versions of the GZipped page being cached.

 

And there you have it - GZipped content is easy to create now in ASP.NET 2.0/.NET 2.0 and if judiciously applied it can save some significant bandwidth. On my homepage which is close to 500k of blog text content (gotta review that <s>) the GZipped size is 55k or so – nearly a 90% reduction in size. I’d say that’s plenty of worth it, especially when caching is added.

Posted in ASP.NET  .NET  

The Voices of Reason


 

Milan Negovan
February 06, 2007

# re: More on GZip compression with ASP.NET Content

Yep, HTML compresses so well because it's highly repetitive.

Like yourself, I've been advocating HTTP compression for a long time and even put together a tool that measures how much bandwidth you can save with various methods and levels of compression: http://www.aspnetresources.com/tools/httpcompression.aspx

Folks in charge of Microsoft.com had better embrace compression. They could be saving ~80%!

Rick Strahl
February 06, 2007

# re: More on GZip compression with ASP.NET Content

Thanks Milan. I just tried this out actually and am kinda curious - what are you using for compression? I tried the Web Log homepage which is now GZip compressed already. Then ran against your analyzer which claims it's reducing the page size down an additional 16% from my original compression. Wonder what's different? You using GZip stream or something else (like XCeed components which are more efficient).

BTW, your site has some great stuff on there! If you haven't gone over to Milan's site do check it out - there are a ton of great articles and a number of useful tools over there.

Pingback
February 07, 2007

# Microsoft .NET Info - a tiddlywiki of .NET related information


A Continuous Learner's Weblog
February 12, 2007

# A Continuous Learner's Weblog: Links (2/6/2007)


PohEe.com
March 01, 2007

# re: More on GZip compression with ASP.NET Content

I tried your way of compression but my WebResource.axd still included into the compression. I gave up and try <a href = "http://www.blowery.org/code/HttpCompressionModule.html" rel="nofollow" target = "_blank" >blowery compression engine</a>. You just have to follow the describe guide at <a href="http://pohee.com/2007/02/http-compression-in-aspnet-20/" rel="nofollow">http-compression-in-aspnet-20</a> to use it. It is a very good engine but <a href = "http://www.madskristensen.dk/blog/CommentView,guid,60533e14-789d-41a1-92d2-43efddce7d8e.aspx" rel="nofollow">Mads Kristensen's engine</a> also good if it enable us to exclude the WebResource.axd compression. cheers :)<a href="" rel="nofollow"></a><a href="" rel="nofollow"></a>

Rick Strahl
March 01, 2007

# re: More on GZip compression with ASP.NET Content

This class doesn't replace WebResource.axd or in any way changes the way that ASP.NET generates resource output. You have to explicitly request a Resource Url for script in order for the code to do any compression.

rrraven
March 05, 2007

# re: More on GZip compression with ASP.NET Content

Ummm... What's wrong with enabling gzip compression for dynamic content in IIS?

Rick Strahl
March 05, 2007

# re: More on GZip compression with ASP.NET Content

Very little control, no caching... check the comments above - somebody mentioned this above.

Ray
May 08, 2007

# re: More on GZip compression with ASP.NET Content

Why not just do Gzip compression with IIS. This can be done by editing the metabase.
http://weblogs.asp.net/owscott/archive/2004/01/12/57916.aspx

# DotNetSlackers: More on GZip compression with ASP.NET Content


Rick Strahl's Web Log
June 23, 2007

# Rick Strahl's Web Log


Arthur
September 11, 2007

# re: More on GZip compression with ASP.NET Content

We are using ICSharpCode library in Sitemap Writer Pro (.NET v1.1) for compression of big sitemap files. It's free and very simple to use.
http://www.sitemapwriter.com

Mike Teather
September 17, 2007

# re: More on GZip compression with ASP.NET Content

Great article thanks! I used this approach for one of my client's web sites and it speeds things up nicely. We did find an error with the code though, try running IE 6.0 with HTTP 1.0, IsGZipSupported() throws an error since AcceptEncoding is null.

The null check below, requires another set of parenthesis, since A and B or C is true if C is true, clearly the intention was A and (B or C).

if (!string.IsNullOrEmpty(AcceptEncoding) &&
AcceptEncoding.Contains("gzip") || AcceptEncoding.Contains("deflate"))

should be:

if (!string.IsNullOrEmpty(AcceptEncoding) &&
(AcceptEncoding.Contains("gzip") || AcceptEncoding.Contains("deflate")))

Anyway, great article, and thanks for the awesome .net website.

Simon Weijgers
September 26, 2007

# re: More on GZip compression with ASP.NET Content

I tried adding this to Application_BeginRequest to gzip everything; (I have a wildcard mapping to map every request to the ASP.NET dll)

If Request.Headers("Accept-Encoding") IsNot Nothing Then
If LCase(Request.Headers("Accept-Encoding")).Contains("gzip") Then
Response.Filter = New GZipStream(Response.Filter, CompressionMode.Compress)
Response.AddHeader("Content-Encoding", "gzip")
ElseIf LCase(Request.Headers("Accept-Encoding")).Contains("deflate") Then
Response.Filter = New DeflateStream(Response.Filter, CompressionMode.Compress)
Response.AddHeader("Content-Encoding", "deflate")
End If
End If

This works fine for the ASPX pages, but for some reason it screws up CSS, and image files. Any ideas?

(note that I can't use IIS compression features because of the wildcard mapping this doesn't seem to work)

wow gold
March 05, 2008

# re: More on GZip compression with ASP.NET Content

Cool, the post.

Thanks for the information.

Nader Khader
March 10, 2008

# re: More on GZip compression with ASP.NET Content

I know the gzip is a good prog Compression But

how can Gzip less size page like homepage with CSS File and JavaScript file

it can compression all files with same time


thanks for your Time

DotNetKicks.com
March 19, 2008

# More on GZip compression with ASP.NET Content

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Sam
March 19, 2008

# re: More on GZip compression with ASP.NET Content

I found this post really useful and put it on my own site. I also submitted it to dot net kicks. Thanks for the info

Mads Kristensen
March 20, 2008

# re: More on GZip compression with ASP.NET Content

Great article. You should consider using deflate instead of GZip if the browser supports it first. The compression rates are exactly the same, but for some reason .NET deflates faster than it GZips. Just swap the order of which encoding you apply to start by asking for deflate support and then GZip afterwards.

Paul Milliken
April 28, 2008

# re: More on GZip compression with ASP.NET Content

Just to let you know, I just had the same issue as Mike Teather (above). Only in my case, the null check failed, so the code went on to try AcceptEncoding.Contains("deflate") on a null AcceptEncoding causing an exception to occur.

That'll teach me for not reading through all the comments before using the code in a project.

Please, can the article be updated to include the extra brackets suggested.

Shikhar
April 30, 2008

# re: More on GZip compression with ASP.NET Content

Its works fine with static pages. But I am facing issue on those pages which have asp.net validation. My validation dosn't work.
Can any one help me out here.

Thanks
Shikhar

Sambo
May 03, 2008

# re: More on GZip compression with ASP.NET Content

Does it work correctly with ASP.NET AJAX?

Arun sharma
May 20, 2008

# re: More on GZip compression with ASP.NET Content

It doesn't work with Ajax. As a matter of fact it disables all client side scripts.

Rick Strahl
June 18, 2008

# re: More on GZip compression with ASP.NET Content

@Arun - ASP.NET Ajax does all sorts of pipeline injection to generate its output so I suspect that that might not work. However, there's nothing inherent in returning AJAX data that might not work, but if a tool mucks with the content type or encoding then anything you override in this regard as this code does will not work.

For example, i use jQuery for my client output and with that I can easily encode all my output via GZip with no problems.

Jamie
July 25, 2008

# re: More on GZip compression with ASP.NET Content

As some above have indicated, this compression should not be used for requests to WebResource.axd. This is how ASP.NET Ajax injects its javascript into your pages and for some reason compressing its output causes a 0 byte file to be sent to the browser, hence the "[Insert Ajax Method Here] is undefined" errors.

If you are just calling the code above in the Page_Init or Page_Load you shouldn't have to worry about this, but if you have the compression code wired up in Global.asax in BeginRequest or something like that, then you'll need to ensure that you skip compression for requests to WebResource.axd.

There are more elegant ways to do this, but to just hardcode an exclusion of WebResource.axd you could do something like this

public static void GZipEncodePage()

{

    if (IsGZipSupported() && 
       HttpContext.Current.Request.Path.IndexOf("WebResource.axd") == -1)
    {
        // snipped.... but do compression here
     }
}

Rick Strahl
July 26, 2008

# re: More on GZip compression with ASP.NET Content

@Jamie - I think it would be a bad idea to wholesale compress pages in your site anyway - this type of approach described here should be selectively used for content IMHO.

I haven't check with MS AJAX output - but shouldn't that be ScriptResource.axd rather than WebResource.axd? The reason that doesn't work most likely is that MS AJAX internally injects into the Http pipeline and applies its own compression so there'd be a problem.

Additional caution may have to be taken with UpdatePanel pages. I'm not sure offhand if this would break UpdatePanel results which also flow through the page pipeline - mainly because it too injects into the page nad filters the Response data.

In short anything that applies Response Filters should not use code the above code for checking whether compression can be applied because the filter order may screw up the compression (especially if another filter already applies compression).

DotNetShoutout
November 19, 2008

# More on GZip compression with ASP.NET Content

Your Story is Submitted - Trackback from DotNetShoutout

Martin
November 20, 2008

# re: More on GZip compression with ASP.NET Content

Hi I'm trying to use this GZipStream stuff

After I managed to attach with my project it worked...

however after some clicking on my site there and then the site simply crashed.. the web page became a lot of garbage text.. to give some example.

� �������`I�%&/m�{J�J��t��`$ؐ@�����iG#)�*��eVe]f@�흼 ��{���{��;�N'���?\fdl��J�ɞ!���?~|?"~� �=�]�~y���yy���E��������w�{���ݧo�����|�<��o�l�mQ-������ ��v���ݫ���սqU_�}���;������v�9������s��ݢ\6�E �>|�P�|��u�͎/�6K��"��˼.ί�/w?J�ղ͗�g��}� ����O��>�߽�{���O�|���}�^N�Ի���㝟|Y_|�Qz7��6����Yせ� ��(��)�[�.Fi�M�b���t����YN_44�6����u��2����ҫ˲X��G��k�I��3�`Z�W�4k ��Qz^�y;�m[җM���'뢜�ҫ<[U� ˲�ȗS�����:GӺ�j��� �i]�0�0�����9ƞM�1�`(�����#�^��:O��u�W�v��uE_V�i�L�٢������hf��~ [�6��8(!oD�X宀~L/�M�u~��G��X6�u��� �p���}�7s��Gi{�"�`~�r���XJ���X��6�4`Ջ��(����i��jqw]O��r��M���L��p������M?K?��x����;��o�t(_Jo$�ӷy�uG?}|W@��aX��I5����W�B�#kV˼}F|��̫�g���P���g��EѴy=���>�Ii֓EA<��~T��^���'����|�m���b�\f�%~���b^�f9q�`�����?y��͛�W�����.�rM ��7C����~

This weird text was given after around 3-4 times clicking ...

what do you think is the problem?

Am I missing something?

Thanks!

Kostenloser WEB Space
December 25, 2008

# Kostenloser WEB Space

Archiv aus Deutschland und aller Welt mit Informationen und Links zum Empfang von Webradio, Web-TV

James Shaw
January 01, 2009

# re: More on GZip compression with ASP.NET Content

Great article Rick! It saved me a lot of time trying to compress pages and other components.

Have a question about the last comment on VaryByCustom in the OutputCache. Can't you use the VaryByContentEncodings attribute of OutputCache?

Loved your simple math captcha below too :).

James Shaw
January 02, 2009

# re: More on GZip compression with ASP.NET Content

Should GZipEncodePage() also do

Response.AppendHeader("Vary", "Accept-Encoding");

even if you don't do output cache, since proxy servers in between may cache (read from pg 33 of Steve Souder's High Performance Websites book :))?

Rick Strahl
January 02, 2009

# re: More on GZip compression with ASP.NET Content

@James - that's a very good point. I'm adding it to my routine. Thanks.

Yovav
February 18, 2009

# RE: (Martin) More on GZip compression with ASP.NET Content

Martin, (by Martin November 20, 2008 @ 1:39 am)

You are getting garbage because your server (or hosting company) is already applying URL compression to your web site,

So your pages are getting compressed twice,

You will probably will see it when removing temp files (or refresh while holding CTRL+F5)

Here is what you probably have setup on your server: http://www.iis.net/ConfigReference/system.webServer/urlCompression


Just add this in your web config to avoid compression conflicts:
<system.webServer>
<urlCompression doStaticCompression="false" doDynamicCompression="false" dynamicCompressionBeforeCache="false" />
</system.webServer>

Paul
April 06, 2009

# re: More on GZip compression with ASP.NET Content

Martin,

Something may also be causing the page_load to occur twice. I found I could use this gzip/deflate into my sitemaster, but not in a page that didn't. For some reason, the page_load is being called twice on a single request. It may have something to do with the page using callbacks, and some asynchronous code. I had to put some extra code in, to not compress on subsequent calls for that page instance.

RickNZ
August 20, 2009

# re: More on GZip compression with ASP.NET Content

You mentioned control over caching as a benefit of this approach. Are you sure that Response.Filter is applied before the page goes into the output cache? From my testing, it seems to be applied after a page is retrieved from the output cache, and before it's sent to the client.

If you want to cache the compressed content, you might consider using IIS compression with <urlCompression dynamicCompressionBeforeCache="true" />

Rick Strahl
August 20, 2009

# re: More on GZip compression with ASP.NET Content

@Rick - you have full control over setting up the cache. Once cached a url resource won't fire again obviously including the filter until the cache expires or the app restarts.

asp.net iis help
March 03, 2010

# re: More on GZip compression with ASP.NET Content

I have a custom implementation of Application_PreRequestHandlerExecute which is applying a delate/gzip filter to the response. However, on IIS7, this is failing on my "script generator" pages. These aspx pages take in Query String values and return a custom bit of script, changing the response type to text/javascript. I think it is failing because of the way iis7 uses mime types, but I'm unsure how to fix it short of turning all compressio off.

Anyone faced this problem?

Mike
March 05, 2010

# re: More on GZip compression with ASP.NET Content

Thanks for the code.

Is there an easy way to see if its working?

Trace does not show anything I can see.

Here is the vb.net code:
Public Shared Sub GZipEncodePage()

Dim Response As HttpResponse = HttpContext.Current.Response

'If (IsGZipSupported() And HttpContext.Current.Request.Path.IndexOf(".axd") = -1) Then
If (IsGZipSupported()) Then

Dim AcceptEncoding As String = HttpContext.Current.Request.Headers("Accept-Encoding")
If (AcceptEncoding.Contains("deflate")) Then
Response.Filter = New System.IO.Compression.DeflateStream(Response.Filter, System.IO.Compression.CompressionMode.Compress)
Response.AppendHeader("Content-Encoding", "deflate")
Else
Response.Filter = New System.IO.Compression.GZipStream(Response.Filter, System.IO.Compression.CompressionMode.Compress)
Response.AppendHeader("Content-Encoding", "gzip")
End If
End If

' Allow proxy servers to cache encoded and unencoded versions separately
Response.AppendHeader("Vary", "Content-Encoding")
Response.AppendHeader("Vary", "Accept-Encoding")
End Sub

Public Shared Function IsGZipSupported() As Boolean
Dim AcceptEncoding As String = HttpContext.Current.Request.Headers("Accept-Encoding")

If (Not String.IsNullOrEmpty(AcceptEncoding) And AcceptEncoding.Contains("gzip") Or AcceptEncoding.Contains("deflate")) Then
Return True
End If

Return False
End Function

Tasarim
July 09, 2010

# re: More on GZip compression with ASP.NET Content

Thanks a million Rick. I've been searching for articles on how to enable the gzip compression on Asp.NET. Everyone seems to have an article about how to enable it on apache. Gzipping content is extremely vital nowadays since on some complex sites, you have to reduce the content size significantly.

Thanks again!

Sandesh
July 14, 2010

# # re: More on GZip compression with ASP.NET Content

Thanks for the artical, I think this link http://www.cmsstores.com/asp-net-file-compression-using-opensource-librar may help some one who is looking to create the zip file using c#.

AlfeG
December 27, 2010

# re: More on GZip compression with ASP.NET Content

Martin,
for problem with garbage encoding there is another workaround.

On Application_Error remove filter from response:

protected void Application_Error(Object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
app.Response.Filter = null;
}

This helps me a lot.

oioi
April 13, 2011

# re: More on GZip compression with ASP.NET Content

Cheers Rick

Bit late to the party but this is very handy. Been trying to figure out how to compress and outputcache pages for ages.

Had a problem with the first two lines of IsGZipSupported() like a few others. I was getting an error (Object reference not set to an instance of an object.) when I was checking what was returned in a client didn't accept compressed responses (using this handy tool http://site-perf.com/)

Changed it to (i use VB)

Dim AcceptEncoding As String = HttpContext.Current.Request.Headers("Accept-Encoding") & ""
If AcceptEncoding <> "" And (AcceptEncoding.Contains("gzip") Or AcceptEncoding.Contains("deflate")) Then

Works great for me now. Thanks a lot dude.

Cheers

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024