Clean URLs with IIS

Last updated on
20 December 2016

Drupal can display brief, "clean" URLs like those at For Apache sites, mod_rewrite powers this feature. On IIS you'll use either a third-party module or (on IIS7) Microsoft's URL Rewrite module to add this functionality. Refer to your specific IIS version below for details.

Some Third Party ISAPI Rewrite Modules


The best method is Microsoft's URL Rewrite Module for IIS7, available via the Web Platform Installer on Windows Server 2008 and Vista. Download and documentation are also available on Microsoft's IIS.Net site. You can also use third party rewrite modules (see list above).

Note: Service Pack 2 for Windows Server 2008 & Vista contained important IIS7 bug fixes (KB954946) that affect how REQUEST_URI works. You can download the patch individually or SP2 from the MS website. IIS7 URL Rewrite Home contains useful explanations and links, including a walkthrough video.

You will also need to enable (if you haven't already) FastCGI.

After setting up the rewrite module and enabling FastCGI you will need to edit your site's web.config file. On IIS7, the web.config file replicates and replaces the functionality of .htaccess (which is included in your Drupal distribution). Here is the web.config file provided with the Acquia Drupal distribution for your reference (if you used the Acquia Drupal Web Platform Installer this is already included):

<?xml version="1.0" encoding="UTF-8"?>
  <!-- Don't show directory listings for URLs which map to a directory. -->
    <directoryBrowse enabled="false" />

       Caching configuration was not delegated by default. Some hosters may not delegate the caching
       configuration to site owners by default and that may cause errors when users install. Uncomment
       this if you want to and are allowed to enable caching
        <add extension=".php" policy="DisableCache" kernelCachePolicy="DisableCache" />
        <add extension=".html" policy="CacheForTimePeriod" kernelCachePolicy="CacheForTimePeriod" duration="14:00:00" />

        <!-- rule name="postinst-redirect" stopProcessing="true">
          <match url="." />
          <action type="Rewrite" url="postinst.php"/>
        </rule -->

        <rule name="Protect files and directories from prying eyes" stopProcessing="true">
          <match url=".(engine|inc|info|install|module|profile|test|po|sh|.sql|postinst.1|theme|tpl(.php)?|xtmpl|svn-base)$|^(|Entries.|Repository|Root|Tag|Template|all-wcprops|entries|format)$" />
          <action type="CustomResponse" statusCode="403" subStatusCode="0" statusReason="Forbidden" statusDescription="Access is forbidden." />
        <rule name="Force simple error message for requests for non-existent favicon.ico" stopProcessing="true">
          <match url="favicon.ico" />
          <action type="CustomResponse" statusCode="404" subStatusCode="1" statusReason="File Not Found" statusDescription="The requested file favicon.ico was not found" />
                <!-- To redirect all users to access the site WITH the 'www.' prefix,
       will be redirected to
                adapt and uncomment the following:   -->
        <rule name="Redirect to add www" stopProcessing="true">
          <match url="^(.)$" ignoreCase="false" />
            <add input="{HTTP_HOST}" pattern="^$" />
          <action type="Redirect" redirectType="Permanent" url="{R:1}" />
                <!-- To redirect all users to access the site WITHOUT the 'www.' prefix,
       will be redirected to
                adapt and uncomment the following:   -->
        <rule name="Redirect to remove www" stopProcessing="true">
          <match url="^(.)$" ignoreCase="false" />
            <add input="{HTTP_HOST}" pattern="^$" />
          <action type="Redirect" redirectType="Permanent" url="{R:1}" />
        <!-- Rewrite URLs of the form 'x' to the form 'index.php?q=x'. -->
        <rule name="Short URLS" stopProcessing="true">
          <match url="^(.*)$" ignoreCase="false" />
            <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
            <add input="{URL}" pattern="^/favicon.ico$" ignoreCase="false" negate="true" />
          <action type="Rewrite" url="index.php?q={R:1}" appendQueryString="true" />

    <!-- httpErrors>
      <remove statusCode="404" subStatusCode="-1" />
      <error statusCode="404" prefixLanguageFilePath="" path="/index.php" responseMode="ExecuteURL" />
    </httpErrors -->

     <!-- Set the default document -->
        <remove value="index.php" />
        <add value="index.php" />
For multi-site configuration, make sure convert each sub-directory into application and assign sufficient user (prefer the same as the one using in master site) to the sub-directory. If application configuration set up inappropriate, an "internal server error" will be posted.


On IIS6, you can use third party modules (see above) to add mod_rewrite-like functionality to IIS. You will also want to download and set up the FastCGI module.


On IIS5, you can use third party modules (see above) to add mod_rewrite-like functionality to IIS. Due to improved application pool security implemented in IIS6+, deployment on IIS6+ is recommended.

IIS5 Alternative: Creating a Custom Error Handler
Note: This method seems to work for IIS5 but not IIS6+.
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 happened 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.php file, which is usually located under sites/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!
    $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/1 and 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