(Because I’m sure that was your exact exclamation when you hit it!)
This week, I was out at a customer site performing an IIS Health Check, and got pulled into a side meeting to look at an app problem on a different box.
The customer was migrating a bunch of apps from older servers onto some shiny new IIS 7.5 servers, and had hit a snag while testing one of these with their new Windows 7 build.
To work around that, they were going to use IE in compatibility mode (via X-UA-Compatible), but adding HTTP response headers caused the app to fail completely and instantly with a 500.19 configuration error.
We tested with a different header (“X-UA-Preposterous”) and it had the same problem, so we know it’s not the header itself.
“Now that’s interesting!”
At first I thought it was app failure, but as it turns out…
The Site Layout
This becomes important – remember I noted that the app was being migrated from an old box to a new one?
Well, on the old box, it was probably one app of many. But the new model is one app per site, so a site was being created for each app.
To allow the app to run without modification to all its paths, a virtual directory was created for /myapp/ (to mimic the old path) which also pointed to the root of newsite.
So myApp above points to c:\inetpub\wwwroot , and so does Default Web Site.
Setting up the problem
So, using the GUI, I set the X-UA-Compatible header to IE=EmulateIE7. The GUI wrote this to the web.config file, as you can see in the status bar at the bottom:
Browsing to a file in the root of the website works absolutely fine. No problem at all.
Browsing to anything in the /myApp/ vdir, though, is instantly fatal:
If you try to open HTTP Response Headers in the /myApp/ virtual directory, you also get a configuration error:
What does that tell us? It tells us that the configuration isn’t valid… interesting… because it’s trying to add a unique key for X-UA-Compatible twice.
Why twice? Where’s it set? We’re at the site level, so we checked the Server level HTTP Response Headers… blank.
But… it’s set in a web.config file, as we can see above. And the web.config file is in the same location as the path.
Ah. So we’re probably processing the same web.config twice, once for each segment of the url!
So, when the user requests something in the root of the site, like http://website/something.asp:
1. IIS (well, the W3WP configuration reader) opens the site’s web.config file, and creates a customheaders collection with a key of X-UA-Compatible
2. IIS Serves the file
And it works. But when the user requests something from the virtual directory as well – like http://website/myApp/something.asp
1. IIS opens the site web.config file, and creates a customheaders collection with a key of X-UA-Compatible
2. IIS opens the virtual directory web.config file (which is the same web.config file) and tries to create the key again, but can’t, because it’s not unique
3. IIS can’t logically resolve the configuration, so responds with an error
Options for Fixing It
1. Don’t use a virtual directory
(or rather, don’t point the virtual directory to the root of the website)
This problem exclusively affects a “looped” configuration file, so if you move the site contents into a physical directory in that path, it’ll just work.
There will be one configuration file per level, the GUI won’t get confused, and nor will the configuration system.
Then you just use a redirecting default.asp or redirect rules to bounce requests from the root to /myApp/ .
2. Clear the collection
You can add a <clear /> element to the web.config file, and that’ll fix it for any individual collection, as shown here:
<add name=”X-UA-Compatible” value=”IE=EmulateIE7″ />
The clear element tells IIS to forget what it thinks it knows already, and just go with it. (When you break inheritance of a collection in the GUI, this is what it does under the covers).
The problem with this approach is that you need to do it manually, and you need to do it for every collection.
In our case, we had Failed Request Tracing rules as well which failed with the same type of error, promptly after fixing the above problem, confirming the diagnosis.
3. Move the configuration
And this splits into two possible approaches:
3a. Editing Applicationhost.config by hand
You can remove the web.config and use a <location path=”New Site/myApp”> tag in applicationhost.config to store configuration, and that’ll work until someone uses web.config again.
3b. Using Feature Delegation
If you do want to prevent web.config being used, you can use the Feature Delegation option to make all properties you don’t want people to set in web.config files Read-Only. (aka “lock” those sections). “Not Delegated” would also work.
This can be done per-Site using Custom Site Delegation, or globally.
And! This has the added happy side-benefit of making the GUI target applicationhost.config, rather than creating or modifying web.config files for that site.
Hope that helps you if you hit it…