In "Virtual Directories: Targeting Local Directories and Network Shares," September 2002, InstantDoc ID 25930, I explain how you can use IIS's virtual-directory structure to selectively publish the content of physical directories without exposing their locations. In that article, I discuss the two most popular content sources: a local directory (i.e., a directory on the Web server) or a network share. A third option exists: redirection from one URL to another. Redirection can help you manage ever-changing content locations and directory structures. To prevent your clients' browsers from running into the infamous URL Not Found error, get to know the basics of HTTP redirection, as well as the range of available options that let you take advantage of IIS's capabilities.
The Basics of Redirection
IIS uses HTTP redirection features to notify clients of new locations for existing URLs. (The lengthy Internet Engineering Task Force—IETF—Request for Comments—RFC—2616 fully describes HTTP.) The structure is fairly simple: A Web client such as Microsoft Internet Explorer (IE) sends a request, and the Web server responds to that request. In its common form, such a request looks like the following:
METHOD URL HTTP_Version HeaderName_1: Header_Value_1 ... HeaderName_N: Header_Value_N BODY
The METHOD attribute (aka the verb) is an action that the client wants the server to perform. GET and POST are the most common methods. GET requests usually don't carry data to the server, although they can send a short amount (i.e., about 1KB) of text-based data in a query string. (A query string is the text string that follows the question mark in a URL. For example, the URL http://altoid/scripts/getparam.asp?fname=leon&lname=braginski contains the query string fname=leon&lname=braginski.) POST requests are made specifically to transfer data to the server. With the POST request, which various HTML forms use, the browser sends data to the server and requests the server to perform an action with that data (e.g., the client sends user data for the server to add to a database). The request's Content-Length header specifies the amount of data that the request can send. This article concentrates on the redirection of GET requests. (To learn about redirecting POST requests, see the Web-exclusive sidebar "Redirecting POST Requests," http://www.windowswebsolutions.com, InstantDoc ID 26302.)
The URL attribute is the HTTP request, which is a path (or an HTML document) relative to the Web server's home directory. For example, the URL for a request to http://altoid/scripts/myscript.asp would be /scripts/myscript.asp.
HTTP_Version is a string identifying the HTTP protocol version that the client is using. The server can reply by using the specified version or an earlier version. (The most recent available version is HTTP 1.1, but a server can reply with HTTP 1.0.) This attribute prevents the server from replying with a version that the browser can't handle.
HeaderName identifies the headers that the HTTP protocol uses to transfer auxiliary information (i.e., Header_Value in the sample request) from the client to the server and vice versa. Some headers are required for each HTTP request (e.g., the Host header for HTTP/1.1 requests, the Location header for 302 Redirect server replies), but many headers are optional. For example, browsers usually identify themselves with the optional User-Agent header. Each header in a request ends with a carriage return and line feed; the final header in a request terminates with a double carriage return and line feed.
The BODY attribute is mandatory for all POST method requests in which the Content-Length header isn't zero. The BODY can be any type of data. For an HTTP form request, the BODY might be a buffer of name=value pairs (e.g., Fname=Leon). When a client encounters the input type=file HTML tag (which is used to upload files to a server), the BODY might be a large binary buffer.
The following output shows a sample GET request. This request for a default document is from a browser using HTTP 1.1.
GET / HTTP/1.1 Host: altoid
After receiving a request, the Web server responds to the client. In its simplest form, a standard response looks like the following:
HTTP_Version STATUS_CODE REASON HeaderName_1: Header_Value_1 ... HeaderName_N: Header_Value_N BODY
STATUS_CODE is a numeric value that defines the status of the completed operation. The standard success reply is 200 OK, as in the following:
HTTP/1.1 200 OK Connection: Close Content-Length: 29 This is the body of the reply
The status code is the most important part of HTTP redirection. IIS natively supports two redirection status codes: 302 Redirect and 301 Move Permanently. You can redirect any virtual directory, physical directory, Web site (by redirecting the root directory), or file to a new target location. When you configure an IIS server to perform redirection, the server sends the 302 Redirect status code (unless you specify permanent redirection) and the Location header, which can represent an absolute URL (e.g., http://server/page.htm) or a relative URL (e.g., /newdir/page.htm). To show that a URL has moved permanently, the server can reply with the 301 Move Permanently status code. These status codes instruct the browser to send a new request to the URL that the Location header specifies. A simple redirection looks like the following:
302 Redirect Location: http://newserver/newurl
Keep in mind that when you configure redirection, you increase the number of requests necessary to get to the specified URL. Browsers will always need to send at least two requests: one to the original URL and one to the new URL.
Know Your Options
Configuring redirection for a virtual directory involves a few steps in the Microsoft Management Console (MMC) Internet Information Services snap-in. First, create a local directory on the IIS server, then open the directory's Properties dialog box and go to the Virtual Directory tab. Select the A redirection to a URL option, and specify the redirection target in the Redirect to text box. The example that Web Figure 1 (http://www.windowswebsolutions.com, InstantDoc ID 26256) shows configures redirection for the sample Scripts virtual directory and specifies the relative URL /newscripts; the absolute URL http://altoid/newscripts would also work. Relative URLs work only for redirection that takes place on the same physical machine. When you want to redirect requests from one machine to another or redirect requests to a Secure Sockets Layer (SSL)enabled site (even one on the same machine), you must use an absolute URL. After I set up the sample redirection that Web Figure 1 shows, the IIS server will redirect requests to http://altoid/scripts/myscript.asp to http://altoid/newscripts/myscript.asp. When a browser receives a 302 Redirect response, the browser sends a new request to the server. Therefore, the final URL's appearance in the browser contains no trace of the original URL. The only proof that redirection took place is in the W3SVC log file, which reveals the originally requested URL, as Web Figure 2 shows.
Notice that in the example that Web Figure 1 shows, I didn't select the The exact URL entered above check box. If I had selected this check box, IIS would always redirect any request directly to http://altoid/newscripts. This action would cause an error if the URL http://altoid/newscripts didn't permit a directory listing or define a default document. The exact URL option works best when you're redirecting specific files, not directories.
When you want to redirect the entire home directory (i.e., the Default Web Site) to a subdirectory below it, select the A directory below this one check box. Be aware that in my tests, this option doesn't work for nested subdirectories, for example to redirect http://server/dir1 to http://server/dir1/subdir1. To redirect the home directory, open the Internet Information Services snap-in. Open the Default Web Site object's Properties dialog box, go to the Home Directory tab, select the A redirection to a URL and A directory below this one check boxes, and enter the relative path of any directory under \inetpub\wwwroot.
To force permanent redirection (i.e., to generate 301 Move Permanently replies instead of 302 Redirect replies), select the A permanent redirection for this resource check box. Be aware, however, that this option doesn't change browser behavior. (Ideally, browsers would not only retrieve new URLs but also update any bookmarks the browsers used to get to the original URL. However, I've yet to see a browser that could perform this task.)
Add Power with Variables and Wildcards
Along with these simple redirection options, you can use several variables and wildcards in the specified redirection target—in conjunction with the The exact URL entered above option—to create a complex, dynamic redirection according to rules you put in place. Each variable represents a part of the original URL, as follows:
- $S represents the original URL's suffix. For example, in a request for http://altoid/scripts/myscript.asp, $S has a value of /myscripts.asp. Because the suffix by itself isn't a valid URL, you can't use the $S variable alone.
- $P represents the original request's query string without the question mark. In a request for http://altoid/scripts/myscript.asp?fname=leon&lname=braginski, $P has a value of fname=leon&lname=braginski.
- $Q represents the same value as $P but includes the question mark. In a request for http://altoid/scripts/myscript.asp?fname=leon&lname=braginski, $Q has a value of ?fname=leon&lname=braginski.
- $V represents the original URL without the server name. In a request for http://altoid/scripts/myscript.asp, $V has a value of /scripts/myscript.asp.
Let's look at an example of how you can use these variables. Suppose you've changed your virtual-directory structure, moving content from the Scripts directory to the Newscripts directory, and asked all users to update their bookmarks. You can configure a tricky redirection to determine how many users are still requesting the original directory and thus to determine when you can safely delete that directory. You can even go one step further and write a script that shows users a friendly reminder to update their bookmarks.
First, configure redirection for the http://altoid/scripts virtual directory. Select the A redirection to a URL and the The exact URL entered above options, then enter the following target:
This target uses the $S and $Q variables to preserve the original parameters of all requests to http://altoid/newscripts, then adds an additional parameter—Redir=1—to the redirection string. For example, this configuration will redirect a request for http://altoid/scripts/redirtest.asp?fname=leon to http://altoid/newscripts/redirtest.asp?fname=leon&Redir=1.
Next, you can write a simple Active Server Pages (ASP) script that determines the presence of the Redir parameter, as Listing 1 shows. Now, attempts to access the original URL will redirect users to the page that Web Figure 3 shows. You can add code to the script to count how many times the script was invoked with the Redir=1 parameter. This count will show how many users are still using the old URL.
IIS redirection also supports wildcards. An asterisk (*) denotes any number of characters in the original URL, with the variables $0 to $9 representing the matched part of the expression. The target URL must start with an asterisk; followed by a portion of the original URL, using the wildcard character; then the matching destination URL, using the $0 to $9 variables. Use semicolons to separate these three sets of characters. For example, suppose you've just installed ASP.NET and want to redirect all requests for .asp files in the Scripts directory to .aspx files in the same directory, preserving all other parameters. As Figure 1, page 4, shows, set up redirection to a URL, select the The exact URL entered above option, and enter the following target:
Upon receiving a request for http://altoid/scripts/getparam.asp?fname=leon, the IIS server will redirect the browser to the new URL http://altoid/scripts/getparam/getparam.aspx?fname=leon, which the server creates dynamically. In this example, $0 takes a value of /getparam, and $Q assumes a value of ?fname=leon. In this example, you could also easily redirect requests to a directory other than Scripts. For example, the target *;*.asp;/newscripts$0.aspx$Q would redirect all requests for .asp pages to .aspx pages in the Newscripts directory.
One final variable exists. The $! variable stops the redirection. Consider the following redirection target:
This target routes all requests for .htm files to default.htm. Therefore, any direct access to default.htm produces an infinite loop of redirections. To prevent this loop, specify a redirection target of $! for the default.htm file.
Add Scripts for Interactivity
Although variables and wildcards offer expanded options, they don't permit true interactivity—for example, redirecting a request according to the client's browser version. To accomplish this type of redirection, however, you need to write an ASP script that performs programmatic redirection through the Response.Redirect ASP method. Such scripts can be simple yet still create a complex redirection according to any business rule you choose.
The script that Listing 2 shows retrieves a User-Agent string (which identifies browser version) from the client, then searches that string for the presence of the MSIE substring (which identifies the browser as IE). If the script locates the MSIE substring, the script displays a welcome message. Otherwise, the script automatically redirects the browser to the IE home page. You can incorporate the code that Listing 2 shows into an ASP application designed to work only with IE. You don't need to configure IIS redirection for this script to work.
You can use the script that Listing 3 shows to set up an interactive Web page that performs redirection according to user selection. Place this .asp file in the virtual directory that permits reading and execution of scripts. When users navigate to the file, they'll see a list of the specified news sites, an option to select a site, and a Go! button. Selecting the desired site and clicking Go! will automatically redirect the browser to the selected site.
A few known problems with IIS redirection exist. One is a failure to redirect correctly when the browser sends a URL that terminates in a forward slash (/), for example http://server/myvdir/. The Microsoft article "HTTP Redirect URL Corruption When Requested URL Contains a Trailing Forward Slash" (http://support.microsoft.com/default.aspx?scid=kb;en-us;q289131) discusses this problem and the workaround. Internet Server API (ISAPI) developers might run into another problem involving long ISAPI extensions. The Microsoft article "IIS May Not Send an Entire Redirect Message When Using an ISAPI Extension" (http://support.microsoft.com/default.aspx?scid=kb;en-us;q296686) delves into this problem's cause and solution.
With only a few caveats, IIS offers excellent HTTP redirection facility. Whatever your skill level, you can use IIS's redirection options—from typing a simple URL to writing a complex ASP script—to direct users to the proper resources.
"IPSec Packet Filtering" (September 2002) includes an incorrect InstantDoc ID. The correct InstantDoc ID for that article is 25935. We apologize for any inconvenience this error might have caused.