Using Clean URLs with IIS
Last modified: April 30, 2009 - 00:22
Drupal can display brief, pretty URLs like those at drupal.org. For Apache sites, mod_rewrite powers this feature. For IIS, you will use a custom error handler for this. You probably want to disable logging in IIS, since every page view is considered an error using this technique.
- make sure your Drupal is working well without clean urls enabled.
- open your Internet Services Manager or MMC and browse to the root directory of the web site where you installed Drupal. You cannot just browse to a subdirectory if you happenned to install to a subdirectory.
- right click and select properties -> custom errors tab
- set the HTTP Error 404 and 405 lines to MessageType=URL, URL=/index.php. If you are using Drupal in a subdirectory, prepend your subdir before /index.php
- paste the following code into the bottom of
settings.phpfile, which is usually located undersites/default/. the first two lines should be edited. If you aren't using a subdirectory, set $sub_directory to "". then set $active=1 and enjoy!<?php
// CONFIGURATION
$sub_dir = "/41/"; // enter a subdirectory, if any. otherwise, use ""
$active = 0; // set to 1 if using clean URLS with IIS
// CODE
if ($active && strstr($_SERVER["QUERY_STRING"], ";")) {
$qs = explode(";", $_SERVER["QUERY_STRING"]);
$url = array_pop($qs);
$parts = parse_url($url);
unset($_GET, $_SERVER['QUERY_STRING']); // remove cruft added by IIS
if ($sub_dir) {
$parts["path"] = substr($parts["path"], strlen($sub_dir));
}
$_GET["q"] = trim($parts["path"], "/");
$_SERVER["REQUEST_URI"] = $parts["path"];
if( array_key_exists( "query", $parts ) && $parts["query"] ) {
$_SERVER["REQUEST_URI"] .= '?'. $parts["query"];
$_SERVER["QUERY_STRING"] = $parts["query"];
$_SERVER["ARGV"] = array($parts["query"]);
parse_str($parts['query'], $arr);
$_GET = array_merge($_GET, $arr);
$_REQUEST = array_merge($_REQUEST, $arr);
}
}
?> - at this point, you should be able to request clean url pages and receive a proper page in response. for example, request the page
/node/1and hopefully you will see your first node shown. you should not use the q= syntax; use the clean url syntax. if you get an IIS error, you have a problem. please fix redo the above and then retest. - browse to index.php?q=admin/system, enable clean URLS, and press Submit.
- you may get a php error if your php error reporting in your php.ini file is set to high. Try this setting in your php.ini file
error_reporting = E_ALL & ~E_NOTICE
Note: This method seems to work for IIS5 but not IIS6. Check this thread for some approaches using ISAPI_Rewrite.
Alternate methods
Alternatively, you can use ISAPI Rewrite by Helicon software to add mod_rewrite-like functionality to IIS:
www.isapirewrite.com
Microsoft has produced a URL Rewrite Module for IIS 7. Download and documentation are on Microsoft's IIS.Net site.

another mod_rewrite
We installed an IIS mod_rewrite.dll
http://www.iismods.com/url-rewrite/documentation.htm
and got it working OK, once I'd tuned the config file a bit.
This mod DOES NOT first check if the file exists, so it must be set to exclude any directories/files you DON'T want rewritten before rewriting everything else.
Like so
Debug 0
Reload 5000
#Browse LOT
#RewriteRule ^/(.*) /index.php
RewriteRule ^/index.php\?q\=(.*)$ /index.php?q=$1 [l]
RewriteRule ^/themes/(.*)$ /themes/$1 [l]
RewriteRule ^/misc/(.*)$ /misc/$1 [l]
RewriteRule ^/css/(.*)$ /css/$1 [l]
RewriteRule ^/files/(.*)$ /files/$1 [l]
RewriteRule ^/images/(.*)$ /images/$1 [l]
# for modules that provide their own js (tinymce,img assist etc)
RewriteRule ^(.*\.js)$ $1 [l]
RewriteRule ^(.*\.gif)$ $1 [l]
RewriteRule ^(.*\.png)$ $1 [l]
RewriteRule ^/modules/tinymce/(.*)$ /modules/tinymce/$1 [l]
# stand-alone
RewriteRule ^/cron.php$ /cron.php [l]
# Handle query strings on the end
RewriteRule ^/(.*)\?(.*)$ /index.php?q=$1&$2 [l]
# now pass through to the generic handler.
RewriteRule ^/(.*)$ /index.php?q=$1 [l]
... this is still under testing, so may need tweaking as I encounter new problems.
.dan.
How to troubleshoot Drupal | http://www.coders.co.nz/
Out of date?
The above handbook entry is well out of date now, and there are several URL manipulation tools now available. See here for an updated rules configuration for using Helicon's ISAPI Rewrite on Drupal 5;
http://drupal.org/node/61367
And here for a complete walkthrough if you not that familiar with Drupal, IIS or ISAPI Rewrite;
http://www.iis-aid.com/articles/how_to_guides/using_drupal_clean_urls_wi...
----------------
Dominic Ryan
www.iis-aid.com
Not for me...
The above handbook and guidelines worked great for me, and I am running Drupal 6.8, hardly outdated. Thanks a lot Drupal!
(By the way, I believe your second link is dead...)
---------------
Josh Weaver
TechMuscle-Inc.com
Updated for ISAPI Rewrite 3.x Lite
Using Drupal Clean URLs with IIS and ISAPI Rewrite Version 3
----------------
Dominic Ryan
www.iis-aid.com
Ionics ISAPI Rewrite Filter
There's also the open-source Ionics ISAPI Rewrite Filter on CodePlex
It requires you to register the ISAPI extension in IIS, and uses one .ini file for all the rewrite rules. It doesn't support .htaccess files.
It also comes bundled with some sample rulesets. From memory there was one for Drupal already.
How to Rewrite Long URLs In IIS
Basically rewriting URLs in IIS is engaged by using the Redirect utility. One must include both the request URL and the destination URL in the "Redirect to:" text box separated by a semi-colon, and, one should also check the box next to "The exact URL entered above".
For example, if one desires to allow the user to enter http://myWeb/shortpath/file.htm but actually map to the file that is located at http://myWeb/long/longer/longest/file.htm one enters
/shortpath/file.htm;/long/longer/longest/file.htm
or, more generally
/shortpath/*;/long/longer/longest/$0
in the "Redirect to:" text box on the folder that is being used as the base for rewriting. In this case the folder named 'shortpath' is being used as the base for rewriting. The shorter alias is referred to as a clean or perhaps friendly URL.
This works in IIS 5.1 and above.
Here's the reference URL: http://support.microsoft.com/kb/324000
Would a picture make this clear?
I think I know what you are saying, but have no actual idea :-} (I'm not doing any IIS this year)
Would you be able to supply a screenshot(s) (if you are talking about some UI dialog) and we can post it into the handbook page? I think that would be helpful to everyone.
.dan.
How to troubleshoot Drupal | http://www.coders.co.nz/
shared windows hosting & friendly url on drupal
This is the easiest working way to create friendly URL on the website. I had to spend quite some time looking for this.
- shared windows hosting server
- PHP 4 & 5 (please use php 5, as register_global is off by default. I didn't have access to php.ini and .htaccess was not allowed to override.)
- IIS 6
- No ISAPI (or any way to install)
- No mod_rewrite
- Drupal 6.6
- no experience
My Config:
I did exactly as it was mentioned here and it worked smoothly! www.thefreeantispyware.com/site/
I tried to install everything. I am not a coder and I do not have any knowledge of code. Infact when I copied the settings, i didnt remove the <?php and got an error in the settings.php file :(
Makes Drupal my favorite CMS now (i've tried joomla, modx, mambo, snews, CS, wordpress, xoops and many more). I was then told that the Drupal community welcomes you with open hands :) I really want to thank you for tihis.
I can't create/edit content
I can't create/edit content after enabling "clean url" on IIS. See http://drupal.org/node/339697#comment-1136710
Got 404 method working on IIS6
Hi,
I had some trouble with this method, but I eventually got it working. I had to do several things.
First off, for context, I'm running Windows 2003/IIS6, with Zend Server (which means I'm using FastCGI, which is the preferred, although not the only, way to run PHP in IIS). It's possible some of the things I did will only work in the FastCGI environment.
The first thing I noted is that it is attempting to recreate $_SERVER['REQUEST_URI'] for some reason, and not doing it accurately at times. But $_SERVER['REQUEST_URI'] was already working just fine for me (IIS or Zend Server populated it for me perfectly), so there was no reason to try and recreate it. So I got rid of the lines that make assignments to $_SERVER['REQUEST_URI'].
The next thing I noted is that $_POST was completely empty, and that this was causing problems--while I could get to any page via GET, none of the forms worked! Big problem. Turns out this is a "feature" in IIS that has to do with it's child execution policy. In a nutshell, when your 404 handler is called, a second request is initiated, and that request is always GET (regardless of what the first request method was--the one that the browser actually made), and it doesn't pass along the POST parameters. More reading about this is available here:
http://www.developmentnow.com/g/59_2006_4_0_0_746187/IIS-6-Form-Post-Dat...
http://blogs.msdn.com/david.wang/archive/2005/11/29/Child_URL_Execution_...
But, I made an interesting discovery. Even though $_POST was empty, and the request method was always GET, I could still read from the stream php://input and get the request body that the browser sent along with it's original request. That's a raw, url-encoded string of key-value pairs. Using PHP's parse_str() function, I was able to parse that raw string into an array, and then assign it to $_POST--and voila, we got our $_POST array back.
I also updated the code that checks to see if we're inside a 404 handler--the code above just looked for the existence of a semicolon, which could result in some false positives. But if we're inside an IIS custom 404 handler, the query string will always start with "404;" and look something like "404;http://mydomain:80/cms/node/1234", so checking to see if $_SERVER['QUERY_STRING'] starts with "404;" seems like a much better check.
So here's my final, working copy of the code above:
<?php
// CONFIGURATION
$sub_dir = "/cms/"; // enter a subdirectory, if any. otherwise, use ""
$active = true; // set to true if using clean URLS with IIS
// CODE
// If we're inside a custom 404 handler, the query string will always start with 404;
// and look something like:
// 404;http://mydomain:80/cms/node/1234
if ($active && substr($_SERVER["QUERY_STRING"],0,4) === "404;") {
// First we need to get the $_POST populated, because IIS will not
// give us that information if we're inside a custom 404 handler.
// The interesting thing is that we can still get to the request body (the raw POST data) through the php://input
// stream, and thus we can use that to recreate the $_POST array.
$recreatedPost = array();
parse_str(file_get_contents("php://input"),$recreatedPost);
$_POST = $recreatedPost;
$qs = explode(";", $_SERVER["QUERY_STRING"]);
$url = array_pop($qs);
$parts = parse_url($url);
unset($_GET, $_SERVER['QUERY_STRING']); // remove cruft added by IIS
if ($sub_dir) {
$parts["path"] = substr($parts["path"], strlen($sub_dir));
}
$_GET["q"] = trim($parts["path"], "/");
if( array_key_exists( "query", $parts ) && $parts["query"] ) {
$_SERVER["REQUEST_URI"] .= '?'. $parts["query"];
$_SERVER["argv"] = array($parts["query"]);
parse_str($parts['query'], $arr);
$_GET = array_merge($_GET, $arr);
$_REQUEST = array_merge($_REQUEST, $arr);
}
}
?>
This is great, because it doesn't require any additional modules and is working completely inside of a base IIS installation.
Requests hang indefinitely?
Hey,
I seem to be running into the same problem (Drupal 6.12, IIS 6). With the 404 redirect as described initially, my site appears to work, but information posted through forms (such as the user login form) seems to be lost. For instance, when you try to login, you simply get the user login form back.
However, when I use your code instead and try again, the requests seems to hang indefinitely (the page never gets rendered). Perhaps the file_get_contents is waiting for an end-of-file which isn't coming? (Just a guess). Any ideas/suggestions? (Tried on Linux/Firefox and Mac/Safari).
Thanks!
Not sure what you did -- IIS is Windows only
Hi Edsko,
Not sure what exactly you're setup is--the code I posted is for getting clean URLs to work on IIS, which is a Windows-only webserver. But you said you tried on Linux and Mac, so you must not be using IIS. What web server are you using?
-Josh
Thanks
This worked for me except in my case the $_SERVER['REQUEST_URI'] *wasn't* already being set....
which meant that although the $_POST array was set ok - Drupal wasn't doing anything with it. (as far as I could see)
so I added that line back in and hey presto - it worked.
Hope this helps someone
Are you using FastCGI?
Hi Killerkent,
What's your system setup--are you using Zend Server, or any other form of FastCGI? I assume you're using IIS for the webserver, since this thread is about getting clean URLs working on IIS...?
-Josh
Yes IIS 6, and....
I just installed the IIS fastcgi extension which I got from here....
http://www.iis.net/extensions/FastCGI (if I remember right)
however I spoke too soon as it *still* doesn't work for me on multipart forms :( - although other simpler forms do work.
Irritating as I only wanted to run ubercart which seems to need imagecache for images which in turn seems to require clean urls.
I don't know if it would fix it, but you could try Zend Server
Hey Kent,
Don't know if it would fix your problem, but you could try Zend Server. The community edition is free.
http://www.zend.com/server
It's what I'm using, and it's working great for me...
-Josh