Need help converting .htaccess to web.config

– Hoping this is the right forum for this –
I have been designing a website for someone over the past month and when it came time to upload it to their site, I was hit with a realization; a realization that they never told me that their host was going to be on an IIS server. Even though they requested a PHP website. So I’m stuck scrambling to learn IIS web.config.

Useful information:
I used This Online .htaccess to Web.Config converter to convert the .htaccess files to web.config at first.
Then I gave in and installed Windows Server 2008 in a VM with URL Rewrite module installed and enabled.

The host my ‘client’ is using is snaphost, with dotNetPanel and is running under shared hosting.

I’ve been wracking my brain over this for the past 2 weeks. Consulted over 50 pages of GOOGLE results as well as yahoo and bing (the latter being the two that I dread the most being that I am Anti-Microsoft).

There are 3 main .htaccess files:

  • First is in the document root /.
  • Second is in the public directory /public
  • Third file is in the /admin directory.

My directory structure looks like this:
/
admin

.htaccess

library
public

.htaccess

.htaccess

Everything works when you view the site at clientsite.dev it works.
When you view a clean URL, such as clientsite.dev/contact, it provides me with a 404 error.(no 404 error after i merge the web.configs together.)
This is the additional information I receive when I view it locally within my Windows 2008 Virtual Machine.

    
[B]Detailed Error Information [/B]
    - [B]Module	IIS[/B] Web Core
    - [B]Notification[/B]	MapRequestHandler
    - [B]Handler[/B]	StaticFile
    - [B]Error Code[/B]	0x80070002
    - [B]Requested URL[/B]	http://clientsite.dev:80/public/contact
    - [B]Physical Path[/B]	C:\\inetpub\\clientsite\\wwwroot\\public\\contact
    - [B]Logon Method[/B]	Anonymous
    - [B]Logon Use[/B]r	Anonymous

[EDIT]
Merged /public/web.config with /web.config and the 404 is gone and pages display properly. Still not how i’d like it and when I do something like POST a form, or log in, I get an “OBJECT MOVED This document may be found <a href=”“>here</a>”. When that message appears, the rest of the page will not render.

The Requested path should be C:\inetpub\clientsite\wwwroot\public\index.php?pagename=contact

The url before rewriting looks like mysite.com/public/index.php?pagename=contact
After rerouting/ to public/ the url will look like mysite.com/index.php?pagename=contact

Where I’m having the issues is when i want the url to look like mysite.com/contact or mysite.com/admin/settings or, thinking ahead, mysite.com/somedir/somefile (which in my original .htaccess files, would work.)

Personally, I would like each directory to have its own .htaccess (like it does now) and its own web.config. That way the rules in the web.config within root “/” does not get affected later on if I decide to automate the web.config creation.

Here are my three .htaccess files

    RewriteEngine On
    Options +FollowSymLinks
    
    #if a match for asset is found, do nothing
    RewriteRule ^library/ - [F]
    RewriteRule ^plugins/ - [F]
    RewriteRule ^admin/ - [L]
    
    RewriteRule ^\\.htaccess$ - [F]
     
    RewriteCond %{REQUEST_URI} =""
    RewriteRule ^.*$ /public/index.php [NC,L]
     
    RewriteCond %{REQUEST_URI} !^/public/.*$
    RewriteRule ^(.*)$ /public/$1
     
    RewriteCond %{REQUEST_FILENAME} -f
    RewriteRule ^.*$ - [NC,L]
     
    RewriteRule ^public/.*$ /public/index.php [NC,L]
 RewriteEngine On
    IndexIgnore *
    # don't show any files in the folder\\
    ErrorDocument 403 /library/error.php?code=403
    ErrorDocument 404 /library/error.php?code=404
    
    RewriteRule ^(community) - [F]
    # #get rid of trailing slashes
    RewriteCond %{HTTP_HOST} ^erttraining\\.clients\\.thunderhawksolutions\\.com$ [NC]
    RewriteRule ^(.+)/$ http://erttraining.clients.thunderhawksolutions.com/$1 [R=301,L]
    
    
    ############
    #CMS RULES #
    ############
    RewriteRule ^registration/register/([0-9]+) index.php?pagename=registration&action=register&cid=$1 [QSA,L]
    RewriteRule ^registration/register index.php?pagename=registration&action=register [QSA,L]
    
    # News 
    RewriteRule ^news/(.*) index.php?pagename=article&articleid=$1 [QSA,L]
    RewriteRule ^news/(.*)/page-([0-9]+) index.php?pagename=article&articleid=$1&page=$2 [QSA,L]
    RewriteRule ^news 	index.php?pagename=news&articleid=$1&page=$2 [QSA,L]
    
    # Login
    RewriteRule ^login(/)?$		index.php?pagename=login&code=0 [QSA,L]
    RewriteRule ^logout(/)?$	index.php?pagename=login&code=1 [QSA,L]
    
    
    # Admin Rules
    RewriteRule ^admin-reg/jsondata.php													admin-scripts/jsondata.php [QSA,L]
    RewriteRule ^admin-reg/(create|remove|manage|update|view)/([^/+].*)/([^/+].*)(/)?	index.php?pagename=admin-reg&action=$2&option=$1&section=$3 [QSA,L]
    RewriteRule ^admin-reg/(create|remove|manage|update|view)/([^+].*)(/)?				index.php?pagename=admin-reg&action=$2&option=$1 [QSA,L]
    
    RewriteRule ^inline-register/([^/+].*)		inline-register.php?courseid=$1 [QSA,L]
    ############
    #CMS RULES #
    ############
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*) index.php?pagename=$1 [QSA,L]

And then there is the admin section:

RewriteEngine On
    DirectorySlash on
    RewriteBase /admin
    
    DirectoryIndex index.php
    
    RewriteRule ^$ index.php?pagename=home [L]
    RewriteRule ^images - [L]
    RewriteRule ^css - [L]
    RewriteRule ^js - [L]
    RewriteRule ^scripts - [L]
    RewriteRule ^db-backups - [L]
    RewriteRule ^multiupload - [L]
    RewriteRule ^server - [L]
    
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !^(images|css|js|scripts)
    	RewriteRule ^(.*)/(.*)/(.*)(/)? index.php?pagename=$1&action=$2&option=$3 [QSA,L]
    	RewriteRule ^(.*)/(.*)(/)? index.php?pagename=$1&action=$2 [QSA,L]
    	RewriteRule ^(.*)$ index.php?pagename=$1 [QSA,L]

This is the resulting web.config file, minus the admin rules (didn’t even get that far with public to add them in):


<configuration>
<system.webServer>
    <rewrite>
        <rules>
                <clear />
                <rule name="Imported Rule 1-1">
                    <match url="^library/" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="CustomResponse" statusCode="403" statusReason="Forbidden" statusDescription="Forbidden" />
                </rule>
               <rule name="Imported Rule 2-1">
                    <match url="^plugins/" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="CustomResponse" statusCode="403" statusReason="Forbidden" statusDescription="Forbidden" />
                </rule>
                <rule name="Imported Rule 3-1" stopProcessing="true">
                    <match url="^admin/" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="None" />
                </rule>
                <rule name="blockAccessToPublic" patternSyntax="Wildcard" stopProcessing="true">
                    <match url="*" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                        <add input="{URL}" pattern="/public/*" />
                    </conditions>
                    <action type="CustomResponse" statusCode="403" statusReason="Forbidden: Access is denied." statusDescription="You do not have permission to view this directory or page using the credentials that you supplied." />
                </rule>
                <rule name="RewriteUserFriendlyURL1" stopProcessing="true">
                    <match url="^([^/]+)/?$" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="public/index.php?pagename={R:1}" />
                </rule>
            <!-- PUBLIC URL REWRITES -->
                <rule name="Imported Rule 1" stopProcessing="true">
                    <match url="^registration/register/([0-9]+)" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Rewrite" url="index.php?pagename=registration&amp;action=register&amp;cid={R:1}" appendQueryString="true" />
                </rule>
                <rule name="Imported Rule 2" stopProcessing="true">
                    <match url="^registration/register" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Rewrite" url="index.php?pagename=registration&amp;action=register" appendQueryString="true" />
                </rule>
                <rule name="Imported Rule 3" stopProcessing="true">
                    <match url="^news/(.*)" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Rewrite" url="index.php?pagename=article&amp;articleid={R:1}" appendQueryString="true" />
                </rule>
                <rule name="Imported Rule 4" stopProcessing="true">
                    <match url="^news/(.*)/page-([0-9]+)" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Rewrite" url="index.php?pagename=article&amp;articleid={R:1}&amp;page={R:2}" appendQueryString="true" />
                </rule>
                <rule name="Imported Rule 5" stopProcessing="true">
                    <match url="^news" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Rewrite" url="index.php?pagename=news&amp;articleid={R:1}&amp;page={R:2}" appendQueryString="true" />
                </rule>
                <rule name="Imported Rule 6" stopProcessing="true">
                    <match url="^login(/)?$" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Rewrite" url="index.php?pagename=login&amp;code=0" appendQueryString="true" />
                </rule>
                <rule name="Imported Rule 7" stopProcessing="true">
                    <match url="^logout(/)?$" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Rewrite" url="index.php?pagename=login&amp;code=1" appendQueryString="true" />
                </rule>
                <rule name="Imported Rule 8" stopProcessing="true">
                    <match url="^admin-reg/jsondata.php" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Rewrite" url="admin-scripts/jsondata.php" appendQueryString="true" />
                </rule>
                <rule name="Imported Rule 9" stopProcessing="true">
                    <match url="^admin-reg/(create|remove|manage|update|view)/([^/+].*)/([^/+].*)(/)?" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Rewrite" url="index.php?pagename=admin-reg&amp;action={R:2}&amp;option={R:1}&amp;section={R:3}" appendQueryString="true" />
                </rule>
                <rule name="Imported Rule 10" stopProcessing="true">
                    <match url="^admin-reg/(create|remove|manage|update|view)/([^+].*)(/)?" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Rewrite" url="index.php?pagename=admin-reg&amp;action={R:2}&amp;option={R:1}" appendQueryString="true" />
                </rule>
                <rule name="Imported Rule 11" stopProcessing="true">
                    <match url="^inline-register/([^/+].*)" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Rewrite" url="inline-register.php?courseid={R:1}" appendQueryString="true" />
                </rule>
                <rule name="RedirectUserFriendlyURL1" stopProcessing="true">
                    <match url="^public/index\\.php$" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                        <add input="{REQUEST_METHOD}" pattern="^POST$" negate="true" />
                        <add input="{QUERY_STRING}" pattern="^pagename=([^=&amp;]+)$" />
                    </conditions>
                    <action type="Redirect" url="{C:1}" appendQueryString="false" />
                </rule>
            <!-- PUBLIC URL REWRITES -->
                <rule name="RewriteRequestsToPublic" stopProcessing="true">
                    <match url="^(.*)$" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
                    <action type="Rewrite" url="public/{R:0}" />
                </rule>
        </rules>
    </rewrite>
</system.webServer>
</configuration>

I am sorry that this is such a long posting but I was seriously not expecting it to be this difficult. I am in need of an immense assortment of help. Though, I think that if I were to solve this issue, I could probably cancel my sessions with a therapist and my room at a mental institution.

P.S: If anyone has any code optimization suggestions for my rewrite rules, that would hopefully also translate well into the web.config, please let me know.

Whioops… could someone move this thread to “Server Configuration, Apache & URL Rewriting”? Thx.

Been a while since I’ve been over there but the IIS url rewriting tool has an option to paste in .htaccess and it will generate the XML for you. That is where I would start.

I started there. It would work, sometimes, other times it wouldn’t. When adding in the admin rules it would completely break with a 500 error. When it does work, it produces an “OBJECT MOVED. This document may be found <a href=”“>here</a>” message that only appears when i proceed to fire my login script for the user at which point it would stop rendering the rest of my code that calls to include the necessary files. While google’ing it I found that it was caused by a header issue.

Still, haven’t found a solution to my problem. Any help would be appreciated.

Sorry, I was on a bit of travel the last week or so.

First I’m not sure what that online converter is doing – you might want want to use the one included in IIS. I pasted your rules into this and it seems I get some different XML:


<rewrite>
  <!--This directive was not converted because it is not supported by IIS: RewriteBase /admin.-->
  <rules>
    <rule name="Imported Rule 1">
      <match url="^library/" ignoreCase="false" />
      <action type="CustomResponse" statusCode="403" statusReason="Forbidden" statusDescription="Forbidden" />
    </rule>
    <rule name="Imported Rule 2">
      <match url="^plugins/" ignoreCase="false" />
      <action type="CustomResponse" statusCode="403" statusReason="Forbidden" statusDescription="Forbidden" />
    </rule>
    <rule name="Imported Rule 3" stopProcessing="true">
      <match url="^admin/" ignoreCase="false" />
      <action type="None" />
    </rule>
    <rule name="Imported Rule 4">
      <match url="^\\.htaccess$" ignoreCase="false" />
      <action type="CustomResponse" statusCode="403" statusReason="Forbidden" statusDescription="Forbidden" />
    </rule>
    <rule name="Imported Rule 5" stopProcessing="true">
      <match url="^.*$" />
      <conditions>
        <add input="{URL}" pattern="^&quot;&quot;$" ignoreCase="false" />
      </conditions>
      <action type="Rewrite" url="/public/index.php" />
    </rule>
    <rule name="Imported Rule 6">
      <match url="^(.*)$" ignoreCase="false" />
      <conditions>
        <add input="{URL}" pattern="^/public/.*$" ignoreCase="false" negate="true" />
      </conditions>
      <action type="Rewrite" url="/public/{R:1}" />
    </rule>
    <rule name="Imported Rule 7" stopProcessing="true">
      <match url="^.*$" />
      <conditions>
        <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" />
      </conditions>
      <action type="None" />
    </rule>
    <rule name="Imported Rule 8" stopProcessing="true">
      <match url="^public/.*$" />
      <action type="Rewrite" url="/public/index.php" />
    </rule>
    <rule name="Imported Rule 9" stopProcessing="true">
      <match url="^registration/register/([0-9]+)" ignoreCase="false" />
      <action type="Rewrite" url="index.php?pagename=registration&amp;action=register&amp;cid={R:1}" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 10" stopProcessing="true">
      <match url="^registration/register" ignoreCase="false" />
      <action type="Rewrite" url="index.php?pagename=registration&amp;action=register" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 11" stopProcessing="true">
      <match url="^news/(.*)" ignoreCase="false" />
      <action type="Rewrite" url="index.php?pagename=article&amp;articleid={R:1}" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 12" stopProcessing="true">
      <match url="^news/(.*)/page-([0-9]+)" ignoreCase="false" />
      <action type="Rewrite" url="index.php?pagename=article&amp;articleid={R:1}&amp;page={R:2}" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 13" stopProcessing="true">
      <match url="^news" ignoreCase="false" />
      <action type="Rewrite" url="index.php?pagename=news&amp;articleid={R:1}&amp;page={R:2}" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 14" stopProcessing="true">
      <match url="^login(/)?$" ignoreCase="false" />
      <action type="Rewrite" url="index.php?pagename=login&amp;code=0" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 15" stopProcessing="true">
      <match url="^logout(/)?$" ignoreCase="false" />
      <action type="Rewrite" url="index.php?pagename=login&amp;code=1" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 16" stopProcessing="true">
      <match url="^admin-reg/jsondata.php" ignoreCase="false" />
      <action type="Rewrite" url="admin-scripts/jsondata.php" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 17" stopProcessing="true">
      <match url="^admin-reg/(create|remove|manage|update|view)/([^/+].*)/([^/+].*)(/)?" ignoreCase="false" />
      <action type="Rewrite" url="index.php?pagename=admin-reg&amp;action={R:2}&amp;option={R:1}&amp;section={R:3}" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 18" stopProcessing="true">
      <match url="^admin-reg/(create|remove|manage|update|view)/([^+].*)(/)?" ignoreCase="false" />
      <action type="Rewrite" url="index.php?pagename=admin-reg&amp;action={R:2}&amp;option={R:1}" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 19" stopProcessing="true">
      <match url="^inline-register/([^/+].*)" ignoreCase="false" />
      <action type="Rewrite" url="inline-register.php?courseid={R:1}" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 20" stopProcessing="true">
      <match url="^(.*)" ignoreCase="false" />
      <conditions>
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
        <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
      </conditions>
      <action type="Rewrite" url="index.php?pagename={R:1}" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 21" stopProcessing="true">
      <match url="^$" ignoreCase="false" />
      <action type="Rewrite" url="index.php?pagename=home" appendQueryString="false" />
    </rule>
    <rule name="Imported Rule 22" stopProcessing="true">
      <match url="^images" ignoreCase="false" />
      <action type="None" />
    </rule>
    <rule name="Imported Rule 23" stopProcessing="true">
      <match url="^css" ignoreCase="false" />
      <action type="None" />
    </rule>
    <rule name="Imported Rule 24" stopProcessing="true">
      <match url="^js" ignoreCase="false" />
      <action type="None" />
    </rule>
    <rule name="Imported Rule 25" stopProcessing="true">
      <match url="^scripts" ignoreCase="false" />
      <action type="None" />
    </rule>
    <rule name="Imported Rule 26" stopProcessing="true">
      <match url="^db-backups" ignoreCase="false" />
      <action type="None" />
    </rule>
    <rule name="Imported Rule 27" stopProcessing="true">
      <match url="^multiupload" ignoreCase="false" />
      <action type="None" />
    </rule>
    <rule name="Imported Rule 28" stopProcessing="true">
      <match url="^server" ignoreCase="false" />
      <action type="None" />
    </rule>
    <rule name="Imported Rule 29" stopProcessing="true">
      <match url="^(.*)/(.*)/(.*)(/)?" ignoreCase="false" />
      <conditions>
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
        <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
        <add input="{REQUEST_FILENAME}" pattern="^(images|css|js|scripts)" ignoreCase="false" negate="true" />
      </conditions>
      <action type="Rewrite" url="index.php?pagename={R:1}&amp;action={R:2}&amp;option={R:3}" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 30" stopProcessing="true">
      <match url="^(.*)/(.*)(/)?" ignoreCase="false" />
      <action type="Rewrite" url="index.php?pagename={R:1}&amp;action={R:2}" appendQueryString="true" />
    </rule>
    <rule name="Imported Rule 31" stopProcessing="true">
      <match url="^(.*)$" ignoreCase="false" />
      <action type="Rewrite" url="index.php?pagename={R:1}" appendQueryString="true" />
    </rule>
  </rules>
</rewrite>

Another approach would be to add local web.configs for the subfolders to separate the namespace a bit to simplify things.

Not sure how much I can help beyond this, I personally avoid url rewrites like the plague – routing is really the way to fly.