Introduction

The following source code is for webmasters who: (1) do not have access or permission to set up and configure an ISAPI filter on MS Windows IIS web server; or (2) do not feel comfortable with the complicated rewrite regular expression syntax most ISAPI filters use.

In several cases the URL can be rewritten or a page redirected without an ISAPI filter, but there are many cases in which you cannot avoid them.

You can learn more about basic 301 redirection and URL rewrites (mod rewrites via .htaccess on Apache web server) here at Cumbrowski.com at my webmaster resources page. There you will also find ISAPI filters and other related tools for Microsoft's Internet Information Server (IIS).

I decided to make some of my ASP code available to the public in response to comments on my article at Search Engine Journal about URL rewrites. I recommend that you read the post and also the comments, if you haven't already done so, because both the article and comments contain useful information on this subject.

Can Do and Can't Do with ASP

The following source code is written in classic active server pages (ASP) and not ASP.NET, but it should not be a problem to port it to the .NET platform.

ASP has a disadvantage compared to PHP, because it does not have the equivalent of $_SERVER['REQUEST_URI'] which returns the path of the original request (excluding the domain name). This allows a much nicer solution than the workaround in ASP for the problem of the default script in a directory and the different URLs from which the same script/page can be accessed.

This brings me to the main purpose of the scripts available on this page. The purpose is primarily related to the issue of Canonical URLs and duplicate content. For a rewrite of the URL to eliminate all dynamic parameters an ISAPI filter is required. However, simple rules would be necessary (not multiple interdependent rules) if you want to cover all scenarios that require a redirect. As a matter of fact, you don't need a redirection rule in your ISAPI filter, but only a simple rewrite rule that disguises to visitors and search engines the true names of your dynamic scripts.

Global Variables

Don't rush into redirecting. Make sure that you keep the number of redirects to the absolute minimum.

You might also want to execute some other code before you do the redirect. This is specifically the case for the code that strips tracking parameters from the URL to redirect to the same page -- without those unique parameters that would be seen as new pages by search engine crawlers. Before you redirect those, the code that is doing the tracking should be executed of course.

Feel free to change the names of the variables throughout the code to match your company or personal coding guidelines.

Host and Port Determination and Check

Here is a global variable which you need to provide to the script. It is the information about your default host/domain. This is important in two cases.

Canonical URLs; and

Multiple domains, but one site.

The first case would be that your site is accessible via www.mysite.com and mysite.com (without the www.). It might be clear to you that both are the same thing, but it is not clear to search engine spiders. As a matter of fact, you could have a different website on each version, because they are technically two different sites.

The second case is common if you own the .net and .com domain for your site or any other TLD (top level domain) variation or your brand name.

If you have any of those domains point to the same site and return the same content, then you must determine which one will be the default, or primary, one.

It triggers a 301 redirect right away, but you can disable that, by simply commenting out the lines "call sysexec301redirect()" and "response.end" by adding a single-quote (') in front of them.

The immediate redirection makes sense if you use tracking where you set cookies. Cookies are bound to a domain. If you set a cookie while the user is still on one of the domains that is not your primary one, the cookie would not be readable to the site after the redirect to the primary host/domain.

It might add an additional 301 redirect, but in some cases this is a smaller problem that a cookie placed on the user's machine without being able to access it again.

'---------------------------------------------------------------------------
' sub: syscheckisprimaryhost
' parameters: none
'---------------------------------------------------------------------------
' system function - checks if the site host is the same as the primary host
' a 301 redirect should be initiated if the function returns true, because
' there is either a canonical-url or multiple domains for the same site problem
'---------------------------------------------------------------------------
sub syscheckisprimaryhost()
dim sprimarysitehost, scurrenthost
sprimarysitehost = lcase(gs301homehost)
sprimarysitehost = replace(sprimarysitehost,"http://","")
if right(sprimarysitehost,1) = "/" then
sprimarysitehost = left(sprimarysitehost,len(sprimarysitehost)-1)
end if
scurrenthost = lcase(request.servervariables("HTTP_HOST"))
if right(scurrenthost,1) = "/" then
scurrenthost = left(scurrenthost,len(scurrenthost)-1)
end if
if scurrenthost <> sprimarysitehost then
gs301redirectpathandscript = request.servervariables("SCRIPT_NAME")
gs301redirectqs = request.querystring
gb301redirect = true
'start immediate redirection code
call sysexec301redirect()
response.end
'end of immediate redirection code
end if
end sub

Generic 301 Redirect Sub Module

The generic sub module that performs the actual 301 redirect, if gb301redirect = true (meaning one or more rules that require redirecting were met) and if all required information such as the destination host, path and script were provided (also global variable).

Trailing Slash Versus No Trailing Slash Issue

Note: For this example am I assuming that "default.asp" is the default script for your homepage or for entry pages of subdirectories of your website. You have to adjust the sample source code if your default script has a different name.

Another problem with duplicate URLs is the way Microsoft IIS works when you have a default script specified. IIS responds to requests to the URLs: "http://www.mysite.com", "http://www.mysite.com/" (trailing slash) and "http://www.mysite.com/default.asp" as the same page.

For requests to "http://www.mysite.com" and "http://www.mysite.com/" IIS automatically executes the default script (default.asp). You can avoid references to "http://www.mysite.com/default.asp" but the problem with the trailing slash versus the non-trailing slash version remains.

There is unfortunately nothing in IIS to avoid this behavior.

Adding to the problem is the fact that none of the request server variables or any other system function in ASP allows you to determine which of the three URLs are requested by the user. It always looks like as if the user requests
"http://www.mysite.com/default.asp", even if the actual request is "http://www.mysite.com" or "http://www.mysite.com/".

PHP has the function $_Server[REQUEST_URI], but unfortunately there is not an equivalent to this function in ASP.

The Workaround
I have found only one solution to this problem. It is not the prettiest one, but it works.

The solution 301 redirects all requests to "http://www.mysite.com/" and "http://www.mysite.com" to the main URL "http://www.mysite.com/default.asp".

Open IIS Manager, right click on the website and select "properties."

Select the tab "documents" and click "add."

Enter as name for the new default content page "index.asp" and press "ok."

Select "index.asp" and click "move up" until "index.asp" is the first script in the list.

Create the file "index.asp" in all folders, including the website root directory.

Note: If you change all websites on the web server, you can make the above change to IIS on the "Web Sites" folder level to update all sites on the web server at once. Also all new websites created on that server will have this setting by default, if you configure it there.

Conclusion
With this solution and the solutions mentioned earlier, all versions of the homepage can be accessed by a user 301-redirected to one single URL, thus no duplicate URL that does not redirect remains on the website. You can test it out for Cumbrowski.com. Check out the following URLs (Note: I set gs301homehost = "www.cumbrowski.com"):

URL Duplication Because of URL Parameters for Custom Tracking

Here is a function to remove URL parameters that are used only for tracking -- without changing or removing the parameters you need -- followed by a 301 redirect without those additional parameters to leverage the "link juice" of those links to your page.

Affiliate Tracking URLs

Many custom solutions and in-house solution providers use them, and networks are often adding (optional in most cases) tracking parameters to the landing page URL on your website.

PPC Tracking URLs

PPC URLs are usually not followed by search engine spiders, but you might want to redirect them as well, because you don't know if the user who hits the PPC landing page (with the custom tracking parameters in the URL) will bookmark the page or maybe copy it and put it up on the web somewhere or send it to a friend via email.

Here is how you use the function in combination with the 301 redirect function, which I provided above.

The variables gs301redirecthost & gs301redirectprotocol were already updated by the function sysdetermine301redirhostandprotocol().

The value for gs301redirectpathandscript was not set unless the function syscheckisprimaryhost() determined that the call was not made to the primary host. I strongly suggest that all tracking URLs used are pointing to the primary host (domain) or things will get a lot more complicated. I will assume in my example that you used the primary host for your tracking URL.

Planned Sample Code for the Future

I will provide more sample source code with detailed explanations of what it does and where and when you should use it -- and why -- as soon as I can. I am currently quite busy, so please bear with me.

I plan to provide and explain the following:

Sample source code for making URLs nice, descriptive and static with a mix of relatively simple ISAPI filter rules and scripting.