Some weeks ago I wrote how to minify and compress the WebResource.axd handler. In the comments of that post someone asked how to do the same with ScriptResource.axd. I thought about it and realized it could be done much smarter.

If you take a look at the HTML source at the CodePlex Issue Tracker page, you’ll see it references 13 WebResource.axd and ScriptResource.axd. That’s 13 different web requests on each page load. The ScriptResource.axd is using HTTP compression but WebResource.axd does not. None of them minifies the scripts.

Therefore it would be cool to create a plug ‘n play HttpModule that combines all resource.axd scripts into one single web request and then minify and compress them. It would result in smaller HTML, fewer web requests and optimized JavaScript code. If you’re a YSlow nazi like me, this is a must-have.

How it works

To make it work, we need both an HttpModule and an HttpHandler.

The module

The module looks for resource scripts in the HTML code and collects all the references from the src tag. It then constructs a new script tag containing all the references and points it to the HttpHandler. The new script tag is injected into the HTML where the first resource script was found. It keeps the order of the scripts intact.

All the original scripts are now removed and a single new script pointing to the handler is injected.

The handler

When the handler is requested by the browser it looks for a URL parameter containing the references to the original resource script. They are separated by a comma in the URL parameter. It then retrieves the content of each script using a HTTP request. Then content is then aggregated into a single string which then get’s minified. Minifying means that all comments and whitespace is removed from the script.

The string is then being cached so the handler only has to do it once per application life cycle. The output is then compressed and the appropriate header set so the browser will cache the file for 30 days. You can easily set the number of days in cache to whatever you see fit.


Download the ScriptCompressor.cs file below and put it in your App_Code folder. Then add the following lines to your web.config:

  <add type="ScriptCompressorModule" name="ScriptCompressorModule" />
  <add verb="*" path="*js.axd" type="ScriptCompressorHandler" />


ScriptCompressor.zip (3.43 kb)


Comment by Chris Pietschmann

Very nice. However I have a couple suggestions that would make this even better...

1) The CodePlex Issue Tracker page has 13 ScriptResource/WebResource .axd script includes. But it also has 9 other javascript includes in the page too. A neat addition to this would be to combine all of them into a single minified script.

2) Maybe have an option to not combine and minify the scripts when the application is running in Debug. This way it's still a little simpler when debugging.

Just a couple thoughts.

Comment by Daze

I added this to my current web application project and everything stopped working. I get Javascript errors as soon as the page loads :(


Comment by Z&#233; Pedro

It seems to be adding the images embedded in the controls and distributed also by the WebResource.axd. Why not check if the Request.ContentType == "application/x-javascript"?

Ps. Nice work...

Z&#233; Pedro

Comment by Z&#233; Pedro

And how about some type of url shrinking? Let's say js.axd?34234234233? It also reduces the ammount of bytes sent to the browser.

Z&#233; Pedro

Comment by Z&#233; Pedro

Sorry for the spam guys, but forget my first remark. Did not see the regular expression searching for script tags :) Damn it, code folding :)

Z&#233; Pedro

Comment by Rush

Does this work for HTTPS sites? For my non-https sites it is working fine but when I move it to my production server with HTTPS it does not work.

Comment by Miron

Hi Mads, Nice idea.
Two things:
The Regex you use in your filter will not match if the type="text/javascript" will come before the src="" part. (as appear in the Blogengine.NET 1.2 release).
Also, if the first WebResource appears in the header (because it contains script for 'onload' event), you will and up with huge script request on the header that will block all the page content until it done. This will perform slower page load than 5 small requests in the page body. The combining must be with more care.

Comment by Rush

I had to comment out an IF statement to get it to work on HTTPS sites.

void context_BeginRequest(object sender, EventArgs e) {
HttpApplication app = sender as HttpApplication;
if (app.Context.CurrentHandler is Page && !app.Request.RawUrl.Contains("serviceframe")) {
//if (!app.Context.Request.Url.Scheme.Contains("https")) {
app.Response.Filter = new WebResourceFilter(app.Response.Filter);

Comment by Rush

Also this solution is limited in IE because links in IE can only be 2048 characters so if you have a lot of webresource.axd files it will not load the ones that come at or after 2048 chars.

Comment by Rush

In the Write method change this If statement to this for the IE fix.

if (index > 0) {
string script = "<script type=\"text/javascript\" src=\"js.axd?path={0}\"></script>";
string path = string.Empty;
string temp = string.Empty;
foreach (string s in list) {
//limit the js.axd?path= link string to 2048 characters in IE
temp += HttpUtility.UrlEncode(s) + ",";
if (HttpContext.Current.Request.UserAgent.Contains("MSIE") && temp.Length > 2036) {
temp = string.Format(script, path) + Environment.NewLine;
html = html.Insert(index, temp);
index += temp.Length;
temp = HttpUtility.UrlEncode(s) + ",";
path = temp;
html = html.Insert(index, string.Format(script, path));

Comment by Tolisss

I get a Sys is not defined error when using it with telerik controls
any thoughs on this one?


Comment by Sam Yong

yep same here. i am getting "Sys is undefined error" when using AJAX Toolkit. any idea how to resolve it?

at first it was the URL too long issue, but suddenly this same along.

Comment by gowrishankar

Hi mads thanks forthe post
but I have a small problem i able to compress all scripts into a single new script pointing to the handler.
but iam getting an error saying Sys is undefined
please help me out


Comment by Cassiano

Hello there! Brilliant idea! Helped me a lot!
Well... for the "Sys is undefined" error, it's very simple =P
The problem is that the link generated (js.axd?path=/WebResource.axd?blablabla) has this damn slash after the "path="! Just had to remove it from the module, something like:

string relative = match.Groups[1].Value;
if (relative[0] == '/') relative = relative.Substring(1);

(Yeah... I know I could have done this in the Regex pattern... but regular expression is one of the things that my brain refuses to understand =P)

AND I needed to add this slash in the handler:

content += RetrieveScript(root + "/" + script) + Environment.NewLine;

Now it's working beatifully!



Comment by GreenShoes

Hi Mads,

I've been scouring the net for something just like this and surprised at the lack of 'lightweight' options as you have presented! Well done! ... well kinda,.. I can't seem to load any ScriptResource.axd references; The response I get is directing me straight to my login page for authentication. Any ideas?

PS: Thanks Cassiano with the WebResource.Axd splash fix: Worked a treat!
PPS: Thanks Rush for the IE fix, but I can't seem to get the CSS implementation working at all :(



Comment by al

hello everyone,

Thank you everybody for putting your minds into this post, especially, Mads!
i stuck with the problem though, wonder if somebody has a hint/idea how to fix it..
i understand we combining those webresources given that all of them JS.. but how about images? In particular, i am suing ajaxtoolkit tab control and it USES webresources to store there images that it require...
So, there are banch of js errors pop up when i try to look at the page with tab control..

using Response.ContentType == "application/x-javascript" as was suggested ealier does not help (or may be i am using it incorrectly)..

Thanks for any help,



Comment by Gopinath

Hi Mads Kristensen
I have used your scriptcompressor.cs file but it didn't work. I got the same error as friends posted above like "Sys is Undefined" in IE and "Sys is not defined" in Firefox. I couldn't do anything in my site. The site was jammed. Please help me how to overcome this issue. Please take a look on this site http:www.anytimerooms.eu which shows an error in IE as well as error console of Firefox.

Advance thanks for your help.

I hope u come with solution asap.


Comment by R

Brilliant bit of code, but I have an issue.
I get a js error Expected ';' and have eventually tracked this down to a missing semicolon in Microsoft's WebForm_DoCallback function

xmlRequestFrame.style.position = "absolute";
xmlRequestFrame.style.top = "-100px"
xmlRequestFrame.style.left = "-100px";

Anybody else come up against this issue?


Comment by Nawaf


I don't think you need to handle the ScriptResource case. Because in the scriptmanager you can set the scriptmode="Release". It will pick the minified version.

The value by default is set to auto, which will look at the config and see if the debug mode is on or not and set it based on that.

Comment by Babu Kumarasamy


I had changed what Cassiano said. But till now also I am getting "Sys is undefined".

[b]This is the script that I am getting from SERVER[/b]


[b]This is from LOCAL[/b]




Comment by Betty

Possible exploit, although I haven't figured out a use for it yet. Your security check simply checks if resource.axd is in the url not the path, meaning it can be in the query string. Meaning the following can be used to get past the check.


That said you can only get files that the webserver is willing to serve up, so it can't be used to get the web.config or anything. However the request does count as the webserver itself rather then the user doing the request meaning you may be able to get items you can't normally.

I'm also having another problem with this seeing as my web host seems to limit the length of my query strings. I tried compressing the querystring with no real results, so I was considering caching the current querystring and using a Guid instead but that wouldn't exactly be ideal (or reliable if the cache dies). Anyone have some brilliant idea around this?

(Accidentally posted this comment on the wrong page blog.madskristensen.dk/.../...riptResourceaxd.aspx)