App_offline.htm gotchas with ASP.NET MVC
July 12, 2010You could be inadvertently exposing your visitors to unfriendly 404 and 403 errors when updating your ASP.NET MVC application, even after deploying an app_offline.htm file!
How app_offline.htm is supposed to work
The ability to take an ASP.NET application temporarily offline by uploading a file named app_offline.htm was a little known, undocumented feature of ASP.NET 2.0 until popularized by Scott Gu’s post about it back in 2005. How it works is simple, to quote Scott:
[If you place a file named app_offline.htm] in the root of a web application directory, ASP.NET 2.0 will shut-down the application, unload the application domain from the server, and stop processing any new incoming requests for that application. ASP.NET will also then respond to all requests for dynamic pages in the application by sending back the content of the app_offline.htm file (for example: you might want to have a “site under construction” or “down for maintenance” message).
Interestingly, this is the second most popular “hidden feature of ASP.NET” on Stack Overflow.
This technique works for traditional ASP.NET 2.0 web forms because typically every url on a site corresponds to an .aspx file. The application gets unloaded and the request for such a dynamic page results in the serving of a friendly “down for maintenance” type of message to any visitor accessing virtually any page of the site.
Try this with an ASP.NET MVC application though, and consider the potential horror:
(Note the 403 error occurs on the application root (“blog” in this case) and the 404 occurs on any MVC Controller Action “page.”)
Why this happens with ASP.NET MVC
Don’t believe me? Go ahead and drop an app_offline.htm file into your application root and… wait… what errors? Everything appears to be working as expected…

Everything seems fine.
I’ll get to this in just a minute, but let’s consider a different scenario where we’re doing a major deployment and perhaps deleting everything including the web.config in the application’s directory first, leaving us with just a lonely app_offline.htm file. The result, alas:
What gives?
Let’s consider the latter scenario where we have an app_offline.htm file and nothing else in our application directory. Though this tells IIS to unload the application and serve up this file in place of dynamic resources such as .aspx pages, it doesn’t change the way it handles other resource requests, and without certain configurations in IIS and/or a web.config file (when present) your visitors may still see 404 or 403 errors.
403 – Forbidden: Access is denied.
If you don’t have “app_offline.htm” listed in the IIS Default Document configuration (and you have directory browsing turned off) you’ll get a 403 error when trying to access the root of a site. IIS simply tries to find the first match for a default document to serve up, and if it doesn’t find one it will return the 403.
You could simply add “app_offline.htm” to the defaut document configuration to get around this error. IIS will find it, and serve up your friendly offline page. Alternatively you could add any .aspx page (such as default.aspx – an empty file is fine) that is configured as a possible default document. This will clue IIS in that an ASP.NET resource is being requested and cause it to serve up the app_offline.htm instead.
404 – File or directory not found.
When you request a dynamic “MVC resource,” such as a Controller Action method with its inherent friendly url (eg. http://kurtschindler.net/blog/post/some-post-here) you’ll get a 404. Think about the consequences here. Even if your application root was showing a friendly “down for maintenance” page, many other potential visitors may be accessing other pages directly and being fooled into thinking they no longer exist!
Again, IIS knows not, and assumes you are requesting a default document in a directory that matches the url specified (eg. it’s looking for a default document under [root]\blog\post\some-post-here). Of course, no such directory or document exists. 404.
In both scenarios, IIS has no clue ASP.NET is involved and thus completely ignores the presence of the app_offline.htm file. So the solution is to tell IIS to runAllManagedModulesForAllRequests so it invokes ASP.NET which then serves up the app_offline.htm page.
The web.config solution
Add a web.config along with your app_offline.htm with just the bare minimum required setting:
<?xml version="1.0"?>
<configuration>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
</configuration>
Now request any resource on your site again and the app_offline.htm takes over as expected!
This web.config is required to tell IIS to enable all managed (ASP.NET) modules to run for all requests and therefore serve up the app_offline.htm for all requests.
In a lot of cases, you’ll likely be taking your site offline while retaining (or copying over instantly) your site’s web.config, and you probably already have runAllManagedModulesForAllRequests set to true or other modules present which dictate that ASP.NET should handle the request. You’ll be error free in this case, but if you ever remove the web.config during the outage you’re in trouble.
Conclusion
The app_offline.htm feature of ASP.NET is not the be all, end all, magical way to turn your entire application off in a friendly way for MVC applications. Because a lot of requests for an MVC application go to resources that first appear not to be managed by ASP.NET (friendly, extension-less urls), it’s vital that IIS be told to invoke ASP.NET for all possible requests, and this requires the presence of a web.config file stating as such.
If you are going to remove your application’s web.config file during site maintenance, you must also include a temporary web.config that specifies runAllManagedModulesForAllRequests=”true” along with the app_offline.htm file.
Tags: app_offline.htm, iis7, mvc, web.config, runAllManagedModulesForAllRequests
Categories: ASP.NET MVC, IIS, Troubleshooting



