App_offline.htm – Page Not Found

September 5, 2009 at 6:11 PMBen

For a few years, .NET has had the built-in capability to easily take your entire application offline when you need to make an update or perform some maintenance on your site.

By simply putting a file named app_offline.htm in the root directory of your site, ASP.NET will serve the app_offline.htm file, instead of the requested page.

I recently employed this feature for probably the first time.  I put the app_offline.htm file on the site, and pulled up my site in Firefox.  The contents of app_offline.htm displayed as expected.

However, if I were to pull my site in Chrome or IE, I would get a Page Not Found error that appeared as though my entire site did not exist.

App_offline.htm result in IE8:

App_offline.htm in IE

App_offline.htm result in Chrome: 

App_offline in Chrome

As mentioned above, in Firefox, the contents of App_offline.htm would display as expected.

The problem is that when ASP.NET serves the App_offline.htm file, the HTTP Response code it passes out is 404.  Chrome will display the page shown above for 404 errors.  In IE, you can actually avoid that generic error page shown above if you turn off HTTP Friendly errors.

But I obviously cannot expect IE visitors to my site to have HTTP Friendly errors turned off.

The way ASP.NET has implemented app_offline.htm by passing out a 404 HTTP status code is not well designed, in my opinion.  A much better implementation would be for ASP.NET to return a normal 200 HTTP status code.

To accomplish this, for this site, I created a simple HTTP Module that processed the beginning of each request.  It checks an “offline” appSetting in web.config to see if the application should be offline.  If the offline setting is turned on, the module will do a server transfer to my own app offline HTML file.

One thing I found on an IIS7 server is requests for items such as JPG, GIF, CSS files, etc. will also go through this HTTP module.  This is normally a great benefit of IIS7’s integrated mode pipeline.  However, if the application offline HTML file includes an IMG tag for an image on the same site, or a link to a CSS file on the same site, the HTTP module is also going to do a server transfer for these other files (JPG, CSS, etc).  This will result in the image not displaying on the application offline page, or the CSS file not loading in the browser, etc.

A simple filter in the HTTP module to only do a server transfer for actual pages is all that is required.  The fairly simple HTTP Module I ended up creating is below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Configuration;
using System.IO;

public class AppOffline : IHttpModule
{
    public void Dispose()
    {
    }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(context_BeginRequest);
    }

    void context_BeginRequest(object sender, EventArgs e)
    {
        HttpContext context = ((HttpApplication)sender).Context;

        if (ConfigurationManager.AppSettings["offline"] == "true")
        {
            string extension = Path.GetExtension(context.Request.Path);

            // Don't server transfer for extensions like .JPG, .CSS, etc.
            string targetedExtensions = ".aspx.ashx.asmx";
            if (targetedExtensions.IndexOf(extension, StringComparison.OrdinalIgnoreCase) == -1)
                return;
            
            context.Server.Transfer("~/application_offline.html");
        }
    }
}

It’s a pretty simple, but effective HTTP module.  When the server transfer is done to my own application offline HTML file, the HTTP status code returned to the client is 200.  No more Page Not Found problems with browsers like IE and Chrome.

Posted in: Development

Tags: ,

Comments are closed