This post describes some of the IIS URL Rewrite Module web.config equivalents of commonly used Apache .htaccess settings. You'll learn how to translate .htaccess content to IIS web.config, this is useful when you need to migrate your website from Apache to Windows Server IIS. The second part of this post outlines how to use Internet Information Services Manager to import and convert .htaccess rules to web.config automatically.

IIS web.config equivalents for Apache's .htaccess (modules like mod_dir, mod_headers, mod_mime mod_rewrite and mod_gzip). Most Apache .htaccess modules are supported in the IIS URL Rewrite module or as web.config configuration directive. You just need to know where to find the correct IIS equivalent for Apache .htaccess, follow the post to find out...

IIS URL Rewrite Module web.config equivalent for Apache mod_dir

http://httpd.apache.org/docs/2.2/mod/mod_dir.html, Provides for "trailing slash" redirects and serving directory index files.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Add trailing slash" stopProcessing="true">
          <match url="(.*[^/])" />
          <conditions>
            <!-- if no file by that name exists -->
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <!-- if a directory by that name does exist -->
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="false" />
          </conditions>
          <action type="Redirect" redirectType="Permanent" url="{R:1}/" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Configure default documents in IIS, or directory index

defaultDocument: list of resources to look for when the client requests a directory

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <defaultDocument>
      <files>
        <!-- delete all currently configured index files -->
        <clear />
        <!-- add first index file -->
        <add value="index.php" />
        <!-- add second index file -->
        <add value="index.html" />
      </files>
    </defaultDocument>
  </system.webServer>
</configuration>

IIS web.config: enable and disable directory browsing

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <!-- set to false to disable -->
    <directoryBrowse enabled="true" />
  </system.webServer>
</configuration>

Apache mod_headers in IIS web.config

http://httpd.apache.org/docs/2.2/mod/mod_headers.html. Customization of HTTP request and response headers

Customize response headers in IIS:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <remove name="ETag"/>
        <!-- Set a Access-Control-Allow-Origin header -->
        <add name="Access-Control-Allow-Origin" value="*"/>
        <!-- Set a X-UA-Compatible header -->
        <add name="X-UA-Compatible" value="IE=Edge,chrome=1"/>
        <!-- remove the X-Powered-By header -->
        <remove name="X-Powered-By"/>
        <!-- Set a Cache-Control header with max-age=691200 value -->
        <add name="Cache-Control" value="max-age=691200" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
</configuration>

Learn how to set an HSTS HTTP Strict-Transport-Security header.

Set MIME-type and charset for file extensions in IIS (Apache mod_mime equivalent)

http://httpd.apache.org/docs/2.2/mod/mod_mime.html. Associates the requested filename's extensions with the file's behavior (handlers and filters) and content (mime-type, language, character set and encoding)

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <httpProtocol>
      <staticContent>
        <remove fileExtension=".html" />
        <mimeMap fileExtension=".html" mimeType="text/html; charset=UTF-8" />
        <remove fileExtension=".htm" />
        <mimeMap fileExtension=".htm" mimeType="text/html; charset=UTF-8" />
        <!-- DON'T set UTF-8 for .css, it breaks markup in Internet Explorer -->
        <!--
          <remove fileExtension=".css" />
          <mimeMap fileExtension=".css" mimeType="text/css; charset=UTF-8" />
        -->
        <remove fileExtension=".css" />
        <mimeMap fileExtension=".css" mimeType="text/css" />
        <remove fileExtension=".js" />
        <mimeMap fileExtension=".js" mimeType="text/javascript; charset=UTF-8" />
      </staticContent>
    </httpProtocol>
  </system.webServer>
</configuration>

IIS URL Rewrite Module equivalent for Apache mod_rewrite (most commonly used variant)

http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html. Provides a rule-based rewriting engine to rewrite requested URLs on the fly

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="wordpress" patternSyntax="Wildcard">
      <match url="*" />
        <conditions>
          <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
          <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
        </conditions>
        <action type="Rewrite" url="index.php" />
    </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Do you use WordPress? Check out my WordPress web.config!

Apache mod_deflate: Gzip compression in IIS, for both static and dynamic file types

http://httpd.apache.org/docs/2.2/mod/mod_deflate.html. Compress content before it is delivered to the client

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <!-- set minFileSizeForComp to 256 kilobytes, the minimum size to allow compression -->
    <httpCompression minFileSizeForComp="256">
      <cheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
      <dynamicTypes>
        <clear />
        <add mimeType="text/*" enabled="true" />
        <add mimeType="message/*" enabled="true" />
        <!-- text/javascript MUST BE the same as in the mimeMap -->
        <add mimeType="text/javascript" enabled="true" />
        <add mimeType="*/*" enabled="false" />
      </dynamicTypes>
      <staticTypes>
        <clear />
        <add mimeType="text/*" enabled="true" />
        <add mimeType="message/*" enabled="true" />
        <!-- text/javascript MUST BE the same as in the mimeMap -->
        <add mimeType="text/javascript" enabled="true" />
        <add mimeType="*/*" enabled="false" />
      </staticTypes>
    </httpCompression>
    <urlCompression doStaticCompression="true" doDynamicCompression="true" dynamicCompressionBeforeCache="true" />
  </system.webServer>
</configuration>

Gzip compression is mostly configured at the web server level and may be ignored in a website's web.config. UrlCompression can be used in web.config files.

Freebies, IIS web.config examples

You can configure a lot of other neat stuff in your IIS web.config file. Three (3) examples.

Set Expire headers to 30 days for static content in IIS

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <httpProtocol>
      <staticContent>
        <!-- Set expire headers to 30 days for static content -->
        <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="30.00:00:00" />
      </staticContent>
    </httpProtocol>
  </system.webServer>
</configuration>

Remove or disable unused handlers

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <!-- remove Perl (.cgi, .pl) if unused -->
      <remove name="PerlPLX" />
      <!-- remove PHP3 (.php3) if unused -->
      <remove name="PHP3" />
      <!-- remove PHP4 (.php4) if unused -->
      <remove name="PHP4" />
      <!-- remove ISAPI_RewriteProxy 64-bit if unused -->
      <remove name="ISAPI_RewriteProxy-64" />
      <!-- remove ISAPI_RewriteProxy if unused -->
      <remove name="ISAPI_RewriteProxy" />
      <!-- remove PHP (.php) if unused -->
      <remove name="PHP" />
    </handlers>
  </system.webServer>
</configuration>

Remove or disable unused IIS-, .NET-modules

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.webServer>
    <modules>
      <!-- remove Helicontech APE -->
      <remove name="Helicon.Ape" />
      <!-- Add Uri- File- and Token cache modules -->
      <!-- for IIS Output Cache -->
      <add name="UriCacheModule" />
      <add name="FileCacheModule" />
      <add name="TokenCacheModule" />
    </modules>
  </system.webServer>
</configuration>

How to use IIS Manager to import .htaccess files

Convert .htaccess to web.config with Internet Information Services (IIS) 7.0 Manager by importing your .htaccess.

IIS Manager is the GUI management tooling for IIS remote administration.

The above mentioned snippets make it easier to convert your Apache mod_rewrite .htaccess rules to web.config, for use on IIS. In this part we'll pursue an easier road: We use the Internet Information Services (IIS) 7.0 Manager, or IIS Manager in short, to import and convert our .htaccess file to web.config automatically.

As an example, we import the Drupal 7.22 .htaccess file and convert it to IIS web.config. Note: Drupal comes with a web.config file, but other CMS-s might not. Therefore it's quite important to know how to convert .htaccess rules to web.config.

About Internet Information Services (IIS) 7.0 Manager

Internet Information Services (IIS) 7 Manager is an administration UI that provides end users and administrators with a way to remotely manage IIS 7.0 and IIS 7.5 servers. You can download it here.

Once you're connected to your website with IIS Manager, click through to URL Rewrite. Just double click the icon.

In the Actions pane, click Import Rules...

In the next screen we can choose our mod_rewrite file. This doesn't necessary have to be a file called .htaccess. Any file name is good, such as htaccess.txt or config.txt for example.


Click the three dots (...) to open up a open file file-browers

and browse to your .htaccess file to open it.

Now click the Import button.

The screen now displays the original Rewrite rules and the converted rules.

Unfortunately, not all rules can be converted because IIS doesn't support all mod_rewrite options. Click an error line to jump to that particular RewriteRule

Comment the unsupported RewriteRule and the result is updated live (apologies for the corrupted images).

If needed, we can always manually convert those rules (there are more unsupported rules in this .htaccess file).

Click Apply (Toepassen) in the Actions pane to apply the imported rules.

Drupal .htaccess source file

We started with the following Drupal .htaccess file:

#
# Apache/PHP/Drupal settings:
#

# Protect files and directories from prying eyes.
<FilesMatch "\.(engine|inc|info|install|make|module|profile|test|po|sh|.*sql|theme|tpl(\.php)?|xtmpl)(|~|\.sw[op]|\.bak|\.orig|\.save)?$|^(\..*|Entries.*|Repository|Root|Tag|Template)$|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig\.save)quot;>
  Order allow,deny
</FilesMatch>

# Don't show directory listings for URLs which map to a directory.
Options -Indexes

# Follow symbolic links in this directory.
Options +FollowSymLinks

# Make Drupal handle any 404 errors.
ErrorDocument 404 /index.php

# Set the default handler.
DirectoryIndex index.php index.html index.htm

# Override PHP settings that cannot be changed at runtime. See
# sites/default/default.settings.php and drupal_environment_initialize() in
# includes/bootstrap.inc for settings that can be changed at runtime.

# PHP 5, Apache 1 and 2.
<IfModule mod_php5.c>
  php_flag magic_quotes_gpc                 off
  php_flag magic_quotes_sybase              off
  php_flag register_globals                 off
  php_flag session.auto_start               off
  php_value mbstring.http_input             pass
  php_value mbstring.http_output            pass
  php_flag mbstring.encoding_translation    off
</IfModule>

# Requires mod_expires to be enabled.
<IfModule mod_expires.c>
  # Enable expirations.
  ExpiresActive On

  # Cache all files for 2 weeks after access (A).
  ExpiresDefault A1209600

  <FilesMatch \.phpgt;
    # Do not allow PHP scripts to be cached unless they explicitly send cache
    # headers themselves. Otherwise all scripts would have to overwrite the
    # headers set by mod_expires if they want another caching behavior. This may
    # fail if an error occurs early in the bootstrap process, and it may cause
    # problems if a non-Drupal PHP file is installed in a subdirectory.
    ExpiresActive Off
  </FilesMatch>
</IfModule>

# Various rewrite rules.
<IfModule mod_rewrite.c>
  RewriteEngine on

  # Set "protossl" to "s" if we were accessed via https://.  This is used later
  # if you enable "www." stripping or enforcement, in order to ensure that
  # you don't bounce between http and https.
  RewriteRule ^ - [E=protossl]
  RewriteCond %{HTTPS} on
  RewriteRule ^ - [E=protossl:s]

  # Block access to "hidden" directories whose names begin with a period. This
  # includes directories used by version control systems such as Subversion or
  # Git to store control files. Files whose names begin with a period, as well
  # as the control files used by CVS, are protected by the FilesMatch directive
  # above.
  #
  # NOTE: This only works when mod_rewrite is loaded. Without mod_rewrite, it is
  # not possible to block access to entire directories from .htaccess, because
  # <DirectoryMatch> is not allowed here.
  #
  # If you do not have mod_rewrite installed, you should remove these
  # directories from your webroot or otherwise protect them from being
  # downloaded.
  RewriteRule "(^|/)\." - [F]

  # If your site can be accessed both with and without the 'www.' prefix, you
  # can use one of the following settings to redirect users to your preferred
  # URL, either WITH or WITHOUT the 'www.' prefix. Choose ONLY one option:
  #
  # To redirect all users to access the site WITH the 'www.' prefix,
  # (http://example.com/... will be redirected to http://www.example.com/...)
  # uncomment the following:
  # RewriteCond %{HTTP_HOST} .
  # RewriteCond %{HTTP_HOST} !^www\. [NC]
  # RewriteRule ^ http%{ENV:protossl}://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
  #
  # To redirect all users to access the site WITHOUT the 'www.' prefix,
  # (http://www.example.com/... will be redirected to http://example.com/...)
  # uncomment the following:
  # RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
  # RewriteRule ^ http%{ENV:protossl}://%1%{REQUEST_URI} [L,R=301]

  # Modify the RewriteBase if you are using Drupal in a subdirectory or in a
  # VirtualDocumentRoot and the rewrite rules are not working properly.
  # For example if your site is at http://example.com/drupal uncomment and
  # modify the following line:
  # RewriteBase /drupal
  #
  # If your site is running in a VirtualDocumentRoot at http://example.com/,
  # uncomment the following line:
  # RewriteBase /

  # Pass all requests not referring directly to files in the filesystem to
  # index.php. Clean URLs are handled in drupal_environment_initialize().
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_URI} !=/favicon.ico
  RewriteRule ^ index.php [L]

  # Rules to correctly serve gzip compressed CSS and JS files.
  # Requires both mod_rewrite and mod_headers to be enabled.
  <IfModule mod_headers.c>
    # Serve gzip compressed CSS files if they exist and the client accepts gzip.
    RewriteCond %{HTTP:Accept-encoding} gzip
    RewriteCond %{REQUEST_FILENAME}\.gz -s
    RewriteRule ^(.*)\.css $1\.css\.gz [QSA]

    # Serve gzip compressed JS files if they exist and the client accepts gzip.
    RewriteCond %{HTTP:Accept-encoding} gzip
    RewriteCond %{REQUEST_FILENAME}\.gz -s
    RewriteRule ^(.*)\.js $1\.js\.gz [QSA]

    # Serve correct content types, and prevent mod_deflate double gzip.
    RewriteRule \.css\.gz$ - [T=text/css,E=no-gzip:1]
    RewriteRule \.js\.gz$ - [T=text/javascript,E=no-gzip:1]

    <FilesMatch "(\.js\.gz|\.css\.gz)quot;>
      # Serve correct encoding type.
      Header set Content-Encoding gzip
      # Force proxies to cache gzipped & non-gzipped css/js files separately.
      Header append Vary Accept-Encoding
    </FilesMatch>
  </IfModule>
</IfModule>

Converted web.config result file

The RewriteRules above converted to, and resulted in, the following IIS web.config file

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="Imported Rule 1">
                    <match url=""(^|/)\."" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{HTTPS}" pattern="on" ignoreCase="false" />
                    </conditions>
                    <action type="CustomResponse" statusCode="403" statusReason="Forbidden" statusDescription="Forbidden" />
                </rule>
                <rule name="Imported Rule 2" stopProcessing="true">
                    <match url="^" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll">
                        <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.icoquot; ignoreCase="false" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="index.php" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

Yes, non URL Rewrite rules were not converted. In order to manually convert, for example, the gzip compressed CSS and JS files, you can use Speed Up WordPress, serve gzip comressed static HTML files on Speed Up WordPress, serve gzip comressed static HTML files as a starting point.

A Gzip example:

<match url="(.*)" ignoreCase="false" />
  <conditions logicalGrouping="MatchAll">
    <add input="{REQUEST_METHOD}" pattern="POST" ignoreCase="false" negate="true" />
    <add input="{HTTP_ACCEPT_ENCODING}" pattern="gzip" ignoreCase="false" />
    <add input="{REQUEST_FILENAME}.gz" matchType="IsFile" />
  </conditions>
  <action type="Rewrite" url="{R:0}.gz" />
</rule>

Apache MultiViews on Windows Server IIS with URL Rewrite module or .htaccess

e.g. hide the .php extension in URL's.

If you'd like to know how to hide the PHP extension in the URL in the address bar of your browser, then read my post hwo to hide .php extension with IIS URL Rewrite Module.

About IIS URL Rewrite Outbound Rules

IIS URL Rewrite also supports so called Outbound Rules. An Outbound Rule is applied to the output stream, from the web server to the client. This makes it possible to rewrite response statuses, HTTP content, and so on. Or perhaps you are interested in .htaccess examples for IIS?

For more information on IIS Outbound Rules, see:

Donate a cup of coffee
Donate a cup of coffee

Thank you very much! <3 ❤️

2 Comments

  1. Hello,

    I have a Drupal 7 site on a (godaddy) shared server.

    It’s based on IIS 7 and it means (as far as I know) IIS ignores setting.php and we have to use web.config

    My goal: I need to get the (real) IP address of the client machine.

    The functions I tested (including ip_address()) are returning me the value of “REMOTE_ADDR” when in my scenario the real IP address of the user is in the the X-Forwarded-For header

    The solution in case of using setting.php is this:

    // Tell Drupal that we are behind a reverse proxy server $conf[‘reverse_proxy’] = TRUE; // List of trusted IPs (IP numbers of our reverse proxies) $conf[‘reverse_proxy_addresses’] = array( ‘127.0.0.1’, );

    Then my question is: How can we make these changes/settings using IIS/web.config file?

    I tried to create a rule (web.config) to rewrite the value of REMOTE_ADDR with the X-Forwarded-Fo but It doesn’t work for me. REMOTE_ADDR is not taking the real IP (X-Forwarded-Fo) of the user. Maybe I need to add something to override this value, before “run” this rule?

    I really appreciate any help with this.

    Regards,

    Leo.

    • Hi Leandro, thank you for your comment.

      I’m not sure what the problem is you’re describing, and I’m not familiar with GoDaddy hosting platform. IIS however, should not ignore Drupal’s settings.php file if PHP is set up correctly, it might ignore the .htaccess file.

      I have a very old Dutch post on how to retrieve the visitors IP address in PHP: https://www.saotn.org/het-remote-address-in-php-uitlezen-proxy-safe/ (the PHP code is self explanatory). An PHP example is:

      $ip = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? 
          $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']);

      Which is what Drupal uses.

      To use a web.config solution you need to have IIS’ Application Request Routing (or ARR) available, as far as I know. Maybe GoDaddy strips the X-Forwarded-For header or replaces it with its own header?

      If GoDaddy is giving you such a hard time (I’ve seen many posts by you about this issue on blogs and SO), just transfer your website to a different hosting company.

Comments are closed