Scott Hanselman

Using ISAPI_Rewrite to canonicalize ASP.NET URLs and remove default.aspx

February 22, '07 Comments [18] Posted in ASP.NET | HttpModule | Musings | Tools
Sponsored By

In the comments of my post on Google PageRanks, Jeff Atwood says:

[The existence of] Default.aspx is another reason to consider URL rewriting. A few of my rewrite rules relative to PR:
- I don't allow links to come in as codinghorror.com, I add the www. if it is not there.
- I remove index.html if it is present

This got me thinking, as it appears that are quite a few ways to get to my home page.

You get the idea...Heck, probably just by mentioning them I'm getting in trouble, right? The URI that dare not speak its name.

Away, if we start by assuming my home page is http://www.hanselman.com/blog/ and that includes the trailing slash. We know that if my browser requests http://www.hanselman.com/blog without the slash, it'll be told by the Web Server to try it again anyway, which is just wasteful.

Apache folks have mod_rewrite and love to remind ASP.NET/IIS folks about their awesomeness. Many sites rely on mod_rewrite for certain behaviors. It's really a fundamental part of the Apache experience. The IIS story becomes better in newer versions of IIS, but the easiest and most flexible way to handle these kinds of things is ISAPI_Rewrite.

Sure, one could create an HTTP Module for ASP.NET for some of this, but at some point you'll realize that you need to catch these requests WAY earlier. Now, ISAPI_Rewrite uses Regular Expressions, and now it's time for my oft-repeated favorite RegEx joke - get ready for it:

"So you've got a problem, and you want to use Regular Expressions to solve it. Now you've got two problems."

Thanks for indulging me. Yes, writing ISAPI_Rewrite stuff is freaking voodoo and I hate it. Once you've written them, they're done. Here's mine:

[ISAPI_Rewrite]
RewriteRule /blog/default\.aspx http\://www.hanselman.com/blog/ [I,RP]

RewriteCond Host: ^hanselman\.com
RewriteRule (.*) http\://www.hanselman.com$1 [I,RP]

RewriteCond Host: ^computerzen\.com
RewriteRule (.*) http\://www.hanselman.com$1 [I,RP]

RewriteCond Host: ^www.computerzen\.com
RewriteRule (.*) http\://www.hanselman.com/blog/ [I,RP]

This rules normalize (canonicalize), to the best of my ability, all the not-really-good URLs above. It'll put everyone to http://www.hanselman.com/blog/ and even take totally lame links like http://computerzen.com/blog/GooglePageRanksConsideredSubtle.aspx and make then "correct." The "I" means "case insensitive" and the "RP" means "Redirect Permanently" - an HTTP 301. If it was just "R" it'd be a 302. When you're testing with ISAPI_Rewrite, always start with "R" to do temporary redirects, because you don't get a second chance with a 301.

So now, even if someone asks for http://www.hanselman.com/blog, they'll be told where to go(here's an HTTP conversation):

  • GET /blog HTTP/1.1
    • Heh, uh, get me /blog, m'kay?
  • HTTP/1.1 301 Moved Permanently
    Location:
    http://www.hanselman.com/blog/
  • GET /blog/ HTTP/1.1
    • Gosh, sorrey (my browser is Canadian) get me /blog/ then.

And it was Good™.

This kind of control is useful in any public facing application or web site and one should take an hour or so and really think about their website's "public face." ISAPI_Rewrite can be a powerful component as part of a larger ASP.NET solution, especially one where Google Ranks do matter and hackable or "pretty" URLs are highly valued.

For us, in the banking industry, having nice URLs like http://www.foobank.com/banking/ or http://mobile.foobank.com makes everyone happy.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Thursday, February 22, 2007 2:12:47 AM UTC
I am using ISAPI Rewrite on a project I recently started. I must say initially it was confusing from a debugging standpoint when the URL in front of you tells you nothing of the page you want. Once you get used to moving HTTPD files around, and the regex involved, it just works.

PS. You put the ™ initial on "And it was Good™" but I didn't get the reference. Please do elaborate :-)
Sushant Bhatia
Thursday, February 22, 2007 2:20:03 AM UTC
Gosh, sorrey (my browser is Canadian) get me /blog/ then.

LOL, love that!
Thursday, February 22, 2007 2:25:06 AM UTC
The ™ is just a joke...as if "And It was Good" (what G*d supposedly said after Creation) was now Trademarked...
Thursday, February 22, 2007 5:10:18 AM UTC
Here's a free ISAPI module that we use for CBC Radio 3.

It works really well. I love the fact that with the new version you can edit the .ini file on the fly and not have to restart the application pool.

Thursday, February 22, 2007 6:34:14 AM UTC
looks like foobank is down.
i'll have to wait till tomorrow to withdraw all my foo.
Thursday, February 22, 2007 7:09:05 AM UTC
Why keep the "www." in front? That's so 1997.
Thursday, February 22, 2007 8:15:19 AM UTC
Call me old fashioned, but I like my URLs with subdomains, like ftp.hanselman.com, mail.hanselman.com, and gopher.hanselman.com.
Thursday, February 22, 2007 2:25:34 PM UTC
Shouldn't the first rule include all QueryString parameters so that you're able to switch to dasBlog's admin view with "/blog/default.aspx?page=admin"? I use IIRF where I needed to apply the rule like this: RewriteRule /blog/default\.aspx(.*) /blog/$1 [I,R=301]

Thursday, February 22, 2007 4:58:13 PM UTC
Alex - not sure...it works now: http://www.hanselman.com/blog/default.aspx?date=2007-02-01

Are you saying it should work like this?

http://www.hanselman.com/blog/?date=2007-02-01
Thursday, February 22, 2007 5:10:41 PM UTC
gopher. ahh, the good old days!
Thursday, February 22, 2007 5:18:58 PM UTC
Scott - IMHO it should work both ways. http://www.hanselman.com/blog/default.aspx?date=2007-02-01 would be shortened to http://www.hanselman.com/blog/?date=2007-02-01.

I noticed this little subtlety after logging in to my blog. The header link was /foo/default.aspx?page=admin but would be redirected to /foo/default.aspx resulting in the admin bar not being shown.
Thursday, February 22, 2007 6:15:54 PM UTC
Alexander - I'm confused...I'm able to get to my admin page with my RegEx (likely due to my ignorance) - what would yours gain me?
Thursday, February 22, 2007 6:38:09 PM UTC
> Why keep the "www." in front? That's so 1997.

Gee, I dunno, why don't you ask http://google.com ?

Oh wait, you can't. Because it redirects to http://www.google.com .

WORLD WIDE WEB BABY! DUBYA DUBYA DUBYA!
Thursday, February 22, 2007 6:51:37 PM UTC
Scott - I suspect it's an IIRF RegEx-specific issue.

RewriteRule /(.*)/default\.aspx /$1 [I,R=301] takes me straight to http://foo/bar/ when clicking a link with href http://foo/bar/default.aspx?page=admin. One way to resolve this either to use RewriteRule ^/(.*)/default\.aspx$ /$1 [I,R=301] to match URLs that contain nothing but a vdir followed by default.aspx or to append QueryStrings like depicted above.

That said, it will gain you nothing but awareness about the differences between ISAPI_Rewrite's and IIRF's RegEx engines.
Thursday, February 22, 2007 7:14:37 PM UTC
Whoops, the rule should read RewriteRule ^/(.*)/default\.aspx$ /$1/ [I,R=301]
Thursday, February 22, 2007 11:30:15 PM UTC
Can all these things be performed without the ISAPI but with a normal httpmodule on an asp.net app?
Or with the default rewriter of asp.net 2.0?

I've the same problem, but hosted on wh4l, so cannot access IIS to install ISAPI
Thursday, February 22, 2007 11:39:29 PM UTC
You *can* but on IIS6 you'd have to do a metabase hack to get ASP.NET associated with all extensions including NO extension...then you've got managed code "in the way" for all requests of all kinds...so, the short answer is no, not unless IIS7.
Friday, February 23, 2007 10:15:56 AM UTC
Ops... that's true... folder are not handled by ASP.NET :)
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.