Search code examples
iis-7asp.net-mvc-4bundlevirtual-directoryasp.net-optimization

Bundling not working for mapped virtual directory


We have a site where CSS and JavaScript are bundled into /Content/ and /Scripts/ directories respectively. Both of these are also mapped targets of virtual directories in a separate domain, which is used for static resources (so cookies aren't served).

The problem is that in our production environment, where compilation -> debug is set to false (so bundling & minification is enabled), the relative URLs work fine and become served from the bundling system, but the static domain URLs (using the virtual directories pointing to the same location) yield HTTP 404 errors.

Is there a way to configure IIS to allow the bundling to work for the contents of this virtual directory? I have visions of having to create a separate dummy MVC project for the static domain so that it recognizes the bundling, but would like to see if there's a better solution.

Here's what the current processing order from IIS seems to be:

  1. Receive incoming request for (bundled) resource, e.g. [static domain]/Content/all.css
  2. Run any HTTP handlers, e.g. bundling if available (none in this case of the static domain)
  3. Follow the virtual directory and serve the resource if available, i.e. searches for ([main domain]/Content/all.css)

Because this file doesn't really exist on the file system, an HTTP 404 is raised. Ideally, step 2 should be running after step 3.

Many thanks.


Solution

  • I've done a bit of experimentation and managed to solve it. What doesn't work is pointing the root of the static site to the same root of the main website, as the web.config files are shared - any change (i.e. disabling session state) made to the static site is also applied to the main website.

    In the end, what works is the following:

    • Have the static domain point to a separate root folder on the file system.
    • Keep the virtual directories on the static site (/Content/ and /Scripts/) pointing to their corresponding locations on the main site.
    • Since we use Helicon's URL-rewriter for cache-busting, create a bare-bones .htaccess file with the same rule as is used in the main site; put that in the root of the static site.
    • Copy the global.asax file from the main website into the root of the static site. This cannot be a shortcut.
    • Copy the contents of the /bin/ folder from the main website into the static site.
      • A virtual directory in IIS pointing to the /bin/ folder on the main website will NOT work.
      • Creating a shortcut in the file system to the /bin/ folder on the main website also will not work.
    • In IIS, ensure the static site uses its own application pool, and that it's configured for .NET 4.0, Integrated pipeline mode. This is so that the MVC 4 bundling will work.
    • Create a bare-bones web.config with the UrlRoutingModule added.

    This is our web.config for the static site:

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <system.web>
            <customErrors mode="Off">
            </customErrors>
            <sessionState mode="Off" />
            <pages enableSessionState="false" enableViewState="false" enableViewStateMac="false" renderAllHiddenFieldsAtTopOfForm="false" />
        </system.web>
        <system.webServer>
            <validation validateIntegratedModeConfiguration="false" />
            <modules runAllManagedModulesForAllRequests="true">
                <remove name="ScriptModule" />
                <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
            </modules>
            <urlCompression doStaticCompression="true" doDynamicCompression="true" />
            <security>
                <requestFiltering allowDoubleEscaping="True" />
            </security>
            <tracing>
                <traceFailedRequests>
                    <add path="*">
                        <traceAreas>
                            <add provider="ASPNET" areas="Infrastructure,Module,Page,AppServices" verbosity="Verbose" />
                        </traceAreas>
                        <failureDefinitions timeTaken="00:00:00" statusCodes="200" />
                    </add>
                </traceFailedRequests>
            </tracing>
            <!-- Cache static content for a month, only enable on UAT or Live -->
            <staticContent>
                <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="30.00:00:00"/>
            </staticContent>
        </system.webServer>
    </configuration>
    

    The main idea is that bundling requires ASP.NET MVC4 to be enabled and running on the static site, since the bundling is evaluated by IIS before the virtual directories are considered (and there doesn't seem a way to reverse that).