DotNetNuke: Installing in Subfolder, Access from Domain Root

Discussion in 'Third-party applications' started by BlueEyedDev, Apr 24, 2011.

  1. I have seen alot of forum posts across the net about how to do this, none of them with any resolution, so I wanted to post my solution to help others looking to do this. While I'm sure others found the solution themselves, I couldn't find anyone actually explain what they did.

    To start with - the constraints I am placing on this solution are:

    • You are on a Windows shared hosting plan using IIS 7.0 or later
    • Your hosting allows you to host unlimited domains, but does not give you a unique webroot for every domain (this is the case at Winhost and it appears DiscountAsp.net has the same setup)
    • You are installing DotNetNuke in a subfolder of your site root
    • Your DNN installation uses the primary domain registered to your shared hosting account
    • You want DotNetNuke to be accessible from your doman root (you want to remove the subdirectory name from your URLs)
    • You have access to URL Rewrite 2.0 or better on IIS and you know how to use it - I won't be providing instructions on how to create the rules. You can use the IIS Remote Management Client or a text editor + FTP, whichever is easiest for you. Point is to create the URL Rewrite rules I'll be pointing out below.
    • You want your user-facing URLs to be clean but you don't really care about your Admin/Host page URLs (these are alot more difficult to clean and I won't spend the time on a non-user-facing cleanup).
    • You have already created your My/MS SQL database for use with your DNN install.
    As some may know (or have figured out), the default install of DNN is not shared-hosting friendly if you want to install in a subfolder (and have DNN accessible from the domain root). The prevailing advice is always to install DNN in the root of your website (e.g. Don't put your DNN instance in a subfolder), which for various reasons is very frequently not possible (as in GoDaddy) or desirable (because you want friendlier URLs for SEO purposes and simply for user ease-of-use). Additionally, if you're on a shared hosting plan with only one web root, you will very quickly pollute your web root with DNN's install, making it messy and difficult to maintain your other applications.

    My interest is not in bashing DNN or defending why its default behavior sucks. People will have their own opinions on how it should behave. Rather, my purpose in this post is to help others do accomplish this with a minimum of pain. I am approaching this very formally in the hope that this thread be pinned, in order to quickly help others looking to do this - in my own search I have seen lots and lots of people want to do this so it is not at all uncommon, but I have seen no complete resolution to the problem anywhere.

    Without further ado.


    Installation
    1. Edit your DNN web.config file to provide your SQL connection string in two places. I won't go into how to do that here or where to get that info from - it is beyond the scope of this document.
    2. Logon to your website using FTP (your main credentials). You should be logged into your web root, usually something like 'www', 'httpdoc' or in the case of Winhost, your root folder name is the same as your login.
    3. Create a directory here called "dnn."
    4. Using either IIS Remote Administration client, or your hosting account's control panel, convert the "dnn" folder to an Application.
    5. Move into the "dnn" directory
    6. Upload your entire DNN install here.
    7. On your local machine, create a web.config file that you will be uploading to your site root.
    8. Open the site root's web.config (which should be empty - you just created it!) and the DNN web.config file (which should have lots and lots of stuff in it, including the connection strings you just edited).
    Move on to the next section.


    Web.Config Files
    To do this, you're going to need the 2 web.config files you opened earlier - one for your site root, the other for your DNN installation (for your DNN installation, you'll be using the web.config file supplied with the DNN install package.)

    Some of the approach here is derived from ASP.NET-ninja Scott Forsyth's blog posts, "Hosting Multiple Domains under One Site", Part 1 and Part 2, in which he explains how to set up the URL rewrite rules to make this happen.


    Web.Config - Site Root
    Switch over to the web.config file you created earlier for your site root. Remember, the web.config file you place in your site root is going to be inherited by every application you create, so it needs to be as efficient as possible - no extraneous processing. The purpose of this file is twofold:

    1. Provide a means to access your DNN installation at the root of your primary domain
    2. Provide a means to create subdomains, host other top level domains, and host other applications within any of your domains, WITHOUT stomping on your DNN installation

    To do this, we are going to use URL Rewrite. This will accomplish the goal of making sure requests get routed to the correct physical/virtual path on your hosting account. Here are the rules in my Web.Config - change the highlighted sections appropriately for your domain(s).


    Code:
    <configuration>
      <!-- The system.webServer section is required for IIS7 compatability It is ignored by IIS6-->
      <system.webServer> 
            <rewrite>
              <rules> 
                <rule name="Rewrite to DotNetNuke Root" stopProcessing="true">
                   <match url="(.*)" />
                   <conditions logicalGrouping="MatchAll">
                     <add input="{HTTP_HOST}" pattern="^(www\.)?mydomain.com$" />
                     <!--<add input="{PATH_INFO}" pattern="^/dnn/" negate="true" />-->
                   </conditions>
                   <action type="Rewrite" url="/dnn/{R:1}" />
                 </rule>
     
                <rule name="Rewrite to Services Root" stopProcessing="true">
                   <match url="(.*)" />
                   <conditions logicalGrouping="MatchAll">
                     <add input="{HTTP_HOST}" pattern="^services.mydomain.com$" />
                     <add input="{PATH_INFO}" pattern="^/Services/" negate="true" />
                   </conditions>
                   <action type="Rewrite" url="/Services/{R:1}" />
                 </rule>
      
                <rule name="Rewrite to otherTLD Root" stopProcessing="true">
                   <match url="(.*)" />
                   <conditions>
                     <add input="{HTTP_HOST}" pattern="^(www\.)?otherTLD.com$" />
                   </conditions>
                   <action type="Rewrite" url="/OtherTLDFolder/{R:1}" />
                 </rule>
     
                 <rule name="Rewrite to subdomain.otherTLD.com" stopProcessing="true">
                   <match url="(.*)" />
                   <conditions>
                     <add input="{HTTP_HOST}" pattern="^subdomain.otherTLD.com$" />
                   </conditions>
                   <action type="Rewrite" url="OtherTLDFolder/subdomain/{R:1}" />
                 </rule>
      
              </rules>
         </rewrite>
       </system.webServer>
    </configuration>

    Explanation

    What does this file do? There are 4 rules here - each one designed to demonstrate a different concept.

    The first rule satisifes condition 1, above - Provide a means to access your DNN installation at the root of your primary domain. In this example I chose the folder "dnn", but I could have just as easily chosen something several levels deep. This rule will allow visitors to visit your domain with or without the "www" at the beginning (www isn't actually needed - it's just an old convention, which is why it is still around. I do not use it on my sites but I support it anyway). You'll notice I have commented out the line <add input="{PATH_INFO}" pattern="^/dnn/" negate="true" />. Under ideal circumstances, this rule would match any path under "dnn" and decide not to process this rule, thus making sure it doesn't get processed again for any path UNDER "dnn" (which would result in 404 errors - the path "/dnn/dnn/blah.aspx" wouldn't exist). However, matching this pattern requires an additional overhead. It would be better to not have to do this, right? This goes back to what I mentioned earlier - because this web.config file will be run for every request - for every application - on every domain you host, it needs to be as efficient as we can make it. Strictly speaking, this rule isn't necessary - is more efficient to REMOVE the rule in subfolders, than to add the additional pattern match at the root. You'll see how we do that later.

    The beauty of this rule is that it will not break true paths, if entered in the browser. What I mean is - if you are rewriting URLs from http://mydomain.com/dnn/Default.aspx to http://mydomain.com/Default.aspx, users will not see errors if they happen to visit http://mydomain.com/dnn/Default.aspx. This is important because some URLs cannot be rewritten, such as javascript-generated links, and the Admin and Host urls in the DNN menus - this is one of the ways that DNN is really not built for a shared hosting scenario by forcing you to install in the site root.

    Finally, the action of the rule is to rewrite the request url to the "dnn" directory, appending anything the match found on the url, after the domain, to the "dnn" path.

    Continued in the next post.
     
    Last edited by a moderator: Oct 14, 2015
  2. The second rule is interesting - one of my requirements will be to host a number of WCF services on a subdomain of my primary TLD and my other TLD's. I would rather keep all web services under a single folder, Services. This second rule accomplishes that by providing a subdomain rewrite - regardless of which TLD is being used. As written, the rule will only match on my primary TLD, but the rule can be easily changed to accomodate any URL [by simply changing mydomainto (.*)]

    The third rule accomplishes something else I see requested VERY frequently on the Winhost forums, which is how to host another TLD on your hosting account without having to have the files point at root. This rewrite rule again looks for the TLD you've specified, and rewrites the request path to the folder of your choose (it can be any depth - I could have used /Other/TLD/Folder/ instead of OtherTLDFolder).

    Finally, the fourth rule allows you to point requests received from a subdomain of your other TLD's to another folder on your account. I didn't really need to use a hierarchical structure here - I could have easily pointed "subdomain.otherTLD.com" to /some/subdomain/of/some/other/TLD. But that would be just silly :)

    Save your site root web.config file, close it and upload it to the site root of your hosting account.


    Web.Config - DotNetNuke Installation

    Now open up the web.config file that came with your DNN install package. You've already edited the connection strings there. Now we're going to change & add a couple of things to fix the URLs generated by DotNetNuke, including redirects.

    First, we want to enable DNN to generate Friendly URLs. By default it will create URLs with long query strongs, or search-engine-optimized URLs. In your DNN web.config file, find the following near the end of the file, in the providers section:

    Code:
    <add name="DNNFriendlyUrl" type="DotNetNuke.Services.Url.FriendlyUrl.DNNFriendlyUrlProvider, DotNetNuke.HttpModules" includePageName="true" regexMatch="[^a-zA-Z0-9 _-]"  />
    and change it to:

    Code:
    <add name="DNNFriendlyUrl" type="DotNetNuke.Services.Url.FriendlyUrl.DNNFriendlyUrlProvider, DotNetNuke.HttpModules" includePageName="true" regexMatch="[^a-zA-Z0-9 _-]" urlFormat="HumanFriendly" />
    Next, find the node that starts

    Code:
    <system.webServer>
    Just before the closing tag of that node, after the "validation" node, you'll add the following rewrite section:

    Code:
    <rewrite>
        <rules>
            <clear />
        </rules>
        <!--
        <outboundRules>
            <rule name="Outgoing - URL paths" preCondition="ASPX Pages Only">
                <match filterByTags="A" pattern="^(?:dnn|(.*//[_a-zA-Z0-9-\.]*)?/dnn)(.*)" />
                <action type="Rewrite" value="{R:1}{R:2}" />
            </rule>
     
            <rule name="response_location URL">
                <match serverVariable="RESPONSE_LOCATION" pattern="^(?:dnn|(.*//[_a-zA-Z0-9-\.]*)?/dnn)(.*)" />
                <action type="Rewrite" value="{R:1}{R:2}" />
            </rule>
     
            <rule name="response_location querystring">
                <match serverVariable="RESPONSE_LOCATION" pattern="(.*)%2fdnn(.*)" />
                <action type="Rewrite" value="{R:1}{R:2}" />
            </rule>
     
            <preConditions>
                <preCondition name="ASPX Pages Only">
                    <add input="{SCRIPT_NAME}" pattern="\.aspx$" />
                </preCondition>
            </preConditions>
        </outboundRules>
        -->
    </rewrite>
    Explanation
    This rewrite is broken into 5 sections.

    The first section accomplishes what I mentioned earlier - making sure the rewrite rules don't run twice in subfolders. This first directive clears all rewrite rules that are inherited from the site root, accomplishing that goal and eliminating the processing overhead of those rules for requests under this path.

    Next is the outboundRules section. Important: Notice that this section has been commented out. You will need these rules, but not until later. If the rules were active now, they would interfere with the DNN install script. Outbound rules allow IIS to edit the links sent back to the user's browser and rewrite the URLs it find that match your conditions. Note: This will add overhead to each request. If your site receives an extremely large volume of traffic, this is probably not a good approach for you, but should be negligible for small/medium sites.

    There are 3 outbound rules, each of which accomplishes a different task. For a full explaination of what they do, read the blog posts by Scott Forsyth that I referenced above. To summarize, the first rule rewrites the path of any URL matching the given tags, in this case, "A" tags (links). The pattern will match your DNN folder regardless of whether absolute or relative URLs are used. Other tags can be matched; if you use the IIS Remote Management client, you can configure the tags to match in great detail.

    The second and third rules are designed to fix redirects - some DNN modules redirect the user to another page, and IIS will intercept those redirects and fix the URLs they send to the user's browser. In particular, the third rule will account for url-encoded paths.

    Finally, the precondition section makes sure that the outgoing URL rewrites only happen on ASPX pages in order to reduce the processing overhead of the rule - you probably don't need to do this for things like CSS, images, and javascript, since users generally don't see those URLs.

    One caveat here is that dynamically-generated URLs are not rewritten. Some DNN skins and templates generate URLs on the fly via javascript. These urls cannot be rewritten - the code itself would have to be changed. That is outside the scope of this tutorial.

    Save this web.config file and upload it to your DNN install folder.


    Setting up your Site
    Now you can visit your DNN installation in the browser. Open up your browser and navigate to http://YOURDOMAIN.com. The DNN install should begin and the wizard will walk you through the process. Once that is done, the following set of steps are very important:

    1. Download the web.config file in your DNN install directory. The install process edits this file so you MUST download it before proceeding.
    2. Open up the DNN web.config file in a text editor.
    3. Find the outboundRules section from above and uncomment it. To make sure this change is picked up by ASP.NET, scroll to the very end of the web.config file and add a blank line to the end - simply press the "Enter" key.
    4. Upload this edited DNN web.config file back to your DNN install directory, overwriting the existing one. The outbound rules should now be activated and MOST of the URLs presented to the user by DNN will appear to be relative to the root of your domain.

    That's it. Visit your site in the browser and enjoy your clean, top-level URLs served from a DNN installed in a subdirectory of your hosting account. :)
     
    Last edited by a moderator: Oct 14, 2015
  3. I should add, as a post-script to the tutorial - I am currently using this configuration with no issues on my site, http://blueeyeddev.com. AJAX postbacks work fine. Redirects work fine. All Admin/Host functions work fine. If you have any issues, post them here and I'm happy to try to help figure out what is going on.

    The trick seemed to be learning what DotNetNuke would puke on and what it wouldn't - the built-in rewrite engine is not written to expect to be stored in a subfolder without seeing that subfolder in the host URLs. The config I've posted above allows you to rewrite URLs without DNN's permission without causing it to break what DNN expects to see in its request paths.

    Anyway, as I said, glad to try to help out if anyone needs help setting this up. DNN is a nice platform but it would be nice if it supported this scenario out of the box (as do most other CMS platforms).
     
  4. works for me

    Thank you, person of respect, saved me who knows how many hours.

    Much appreciated! Right on time too, you posted about 8 hours before I went looking.

    A most necessary workaround for what I agree with you is a design flaw in DNN.

    But I can understand how this came to be: when you are head down in development, it is sometimes hard to remember that not everyone is as focused on your particular solution as you are, and might want to have other things in place besides your own product. Mea maxima culpa and like that.

    best--mike
     
  5. Ray

    Ray

    Yes, thank you. If we have the Posting of the day award, you've certainly would have received it. Many thanks for your hard work you put into it.
     
  6. tnx honey
     

Share This Page