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.
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.
You can download the source code of all examples used in this article.
Here is the zip-file with the code: asp301code.zip (right click and select "save as").
Additionally, before each snippet of source code is a link with this icon that refers to the particular piece of source code in plain text format.
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.
Figure #1'global variables for 301 redirect dim gb301redirect,_ 'true/false - true is 301 redirect must be performed gs301redirectprotocol,_ 'secured (https://) = port 443 or unsecured page = port 80 gs301redirecthost,_ 'the redirect host (or domain) gs301redirectpathandscript,_ 'the path (sub directories) and script name gs301redirectqs,_ 'the url parameters or query string gs301homehost 'default host name (only relevant if you have multiple 'domains pointing to one website)
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.
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.
Figure #2'Home Host - you could also make www.mysite.com the Home host 'if you prefer users to redirect to http://www.mysite.com 'rather than http://mysite.com gs301homehost = "mysite.com"
This function simply double-checks your setting for gs301homehost
and serializes it. It also determines whether the current page is called secured or unsecured.
Updated Global Variables
gs301redirectprotocol
gs301redirecthost
'--------------------------------------------------------------------------- ' function: sysdetermine301redirhostandprotocol ' parameters: none '--------------------------------------------------------------------------- ' system function - determines the primary and destination host ' (e.g. www.mysite.com or mysite.com ) and protocol ' http:// for port 80 and https:// for port 443 '--------------------------------------------------------------------------- sub sysdetermine301redirhostandprotocol() dim sprimarysitehost sprimarysitehost = lcase(gs301homehost) sprimarysitehost = replace(sprimarysitehost,"http://","") sprimarysitehost = replace(sprimarysitehost,"https://","") if right(sprimarysitehost,1) = "/" then sprimarysitehost = left(sprimarysitehost,len(sprimarysitehost)-1) end if gs301redirecthost = sprimarysitehost if request.servervariables("SERVER_PORT") = 80 then gs301redirectprotocol = "http://" end if if request.servervariables("SERVER_PORT") = 443 then gs301redirectprotocol = "https://" end if end sub
This sub module checks whether the current host matches your specified primary host or not.
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.
Figure #4'--------------------------------------------------------------------------- ' 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
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).
'-------------------------------------------------------------------------------- 'generic 301 redirect sub 'requires global variables: gb301redirect , gs301redirectprotocol 'gs301redirecthost , gs301redirectpathandscript and gs301redirectqs '-------------------------------------------------------------------------------- sub sysexec301redirect() dim s301redirecturl if gb301redirect = true then '301 redirect is enabled 'check that all needed information were provided if gs301redirectprotocol <> "" and gs301redirecthost <> "" and _ gs301redirectpathandscript <> "" then s301redirecturl = gs301redirectprotocol & gs301redirecthost s301redirecturl = s301redirecturl & gs301redirectpathandscript if gs301redirectqs <> "" then 'querystring s301redirecturl = s301redirecturl & "?" & gs301redirectqs end if response.status = "301 moved permanently" response.addheader "location", s301redirecturl response.end end if end if end sub
Here is a little sample where all the current functions I introduced are used together. The current functions are the foundation for more functions that will follow soon.
Figure #6'global variables for 301 redirect dim gb301redirect,_ 'true/false - true = 301 redirect must be performed gs301redirectprotocol,_ 'secured (https://) = port 443 or unsecured = port 80 gs301redirecthost,_ 'the redirect host (or domain) gs301redirectpathandscript,_ 'the path (sub directories) and script name gs301redirectqs,_ 'the url parameters or query string gs301homehost 'default host name (only relevant if you have 'multiple domains pointing to one website) gs301homehost = "mysite.com" Call sysdetermine301redirhostandprotocol() Call syscheckisprimaryhost()
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
".
index.asp
" and press "ok."index.asp
" and click "move up" until "index.asp
" is the first script in the list.index.asp
" in all folders, including the website root directory.index.asp
" looks like this:<% Dim s301RedirectHOST, s301RedirectURL s301RedirectHOST = Request.Servervariables("HTTP_HOST") s301RedirectURL = "http://" & s301RedirectHOST & Request.Servervariables("SCRIPT_NAME") s301RedirectURL = replace(s301RedirectURL,"index.asp","default.asp",1,-1,1) Response.Buffer = False Response.Status = "301 Moved Permanently" Response.AddHeader "Location", s301RedirectURL Response.End %>
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"
):
As you can see are they all being redirected to the "master URL" at "http://www.cumbrowski.com/default.asp
".
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 URLsMany 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 URLsPPC 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.
Figure #8Function RemoveFromQueryStr(ByVal strCurrentQueryString, ByVal strParamName) Dim strQueryString, strItem, strParamArray strParamArray = Split(strCurrentQueryString, "&") For Each strItem In strParamArray On Error Resume Next If Not Ucase(Split(strItem, "=")(0)) = Ucase(strParamName) Then strQueryString = strQueryString & strItem & "&" Else gb301redirect = true End If On Error Goto 0 Next strQueryString = Replace(strQueryString, "&&", "&") RemoveFromQueryStr = strQueryString End Function
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.
'global variables for 301 redirect dim gb301redirect,_ 'true/false - true = 301 redirect must be performed gs301redirectprotocol,_ 'secured (https://) = port 443 or unsecured = port 80 gs301redirecthost,_ 'the redirect host (or domain) gs301redirectpathandscript,_ 'the path (sub directories) and script name gs301redirectqs,_ 'the url parameters or query string gs301homehost 'default host name (only relevant if you have 'multiple domains pointing to one website) gs301homehost = "mysite.com" Call sysdetermine301redirhostandprotocol() Call syscheckisprimaryhost() if gs301redirectpathandscript = "" then gs301redirectpathandscript = request.servervariables("SCRIPT_NAME") end if gs301redirectqs = request.querystring if request.querystring("afid") <> "" then 'Your custom tracking code where you log hits for each afid, set a cookie etc. '.... end if 'Remove afid tracking URL parameter from Querystring gs301redirectqs = RemoveFromQueryStr(gs301redirectqs, "afid") 'if afid was found in querystring, gb301redirect is set to true if gb301redirect = true then Call sysexec301redirect() end if
You can call the function multiple times if you have more than one possible tracking parameter in the URL.
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.
You will require Helicon URLRewrite for those examples.
See the Advertiser Kit to learn more about sponsorship opportunities at Cumbrowski.com. Press? Download my Media Kit.