Take an ASP.NET Application offline with HttpModules

by Arnold Matusz 2 9 2009

Whenever you do maintenance work on a website it is advisable to show the visitors a nice message telling them politely to come back later, rather than a nasty error, or even worse: a big Yellow Screen of Death.

Since ASP.NET 2.0 came out of the labs of Microsoft, there is a way to take a web application down using the “app_offline.htm” approach. You simply create a HTM file, which you then upload to the server, and if there is any request to this web application, IIS will automatically show the contents of the app_offline.htm file. Once uploaded, most people only rename the file so that it doesn't catch up with IIS anymore, and the site is already back online.

This approach comes in really handy when you upload new files to the server, and while the upload did not finish, there are already requests coming in. (Ex: somebody accesses your website - ~/default.aspx, and you have some logic in this page that references a class that has not been uploaded yet)

Where this approach simply doesn’t cut the mustard is when you want to restrict the access for visitors, but you still want to be able to access the site as an administrator (testing, problem investigation, general maintenance). The app_offline.htm approach is not good for this because simply everybody will get the content of the app_offline.htm file no matter what the requested page is.

In the quest for a good solution to this problem I had the following facts in mind:

  • same sort of easy use like uploading the app_offline.htm (maybe a similar file to trigger the action)
  • access granted based on the ip of the visitor (if AdminIP == RequestIP –> unrestricted surfing)

My current solution looks like this:

  • The filter is triggered when a file named “offline.html” exists on the server. (This is practically the same idea as the app_offline.htm one)
  • The web.config file contains an application settings key defining the Administrator IP
<add key="AdminIP" value="123.124.125.126" />
  • To filter the unwanted IP addresses out, I’m using a HttpModule:
/// <summary>
/// This is how you take an ASP.NET application offline the 
/// Arnold Matusz way with AppOfflineModule
/// </summary>
public class AppOfflineModule : IHttpModule
{
    public void Dispose()
    {
        
    }

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

    void context_BeginRequest(object sender, EventArgs e)
    {
        HttpApplication application = (HttpApplication)sender;
        HttpContext context = application.Context;
        if (File.Exists(Path.Combine(context.Server.MapPath("~"), "offline.html")))
        {
            string ip = context.Request.UserHostAddress;
            string adminIP = ConfigurationManager.AppSettings["AdminIP"];

            if (ip != adminIP)
            {
                context.RewritePath("~/offline.html");
            }
        }
    }
}

As you can immediately see, on each Request, I’m performing a check for a file named offline.html with File.Exists(). If it exists I’m checking for the administrator IP in the web.config file to see if the request is coming for somebody that should not be restricted.

Adding your HttpModule is easy:

<httpModules>
  ...
  <add name="AppOfflineModule" type="AppOfflineModule" />
  ...
</httpModules>

Once offline.html uploaded, each “restricted” visitor will only see the content of offline.html.

To disable the whole functionality you only need to rename the offline.html file to anything else (I suggest a difference of a character so that it can be easily renamed back: offlin.html).

In a very small maintenance group this solution works just fine, but for future work I’d definitely create some functionality to define more IP addresses which can access the website while it’s taken “offline”. Defining an IP class would also be another good idea, particularly when the number of “administrators” is very big.

There is little limit in the possibilities to identify who can and cannot access your website if you use this approach, you only need to program your logic into a BeginRequest event, and you are ready to go.

DZone it Digg it Submit to StumbleUpon Submit to Technorati Submit to reddit.com Submit to del.icio.us Submit to NewsVine Submit to Furl Submit to BlinkList

Tags: , ,

ASP.NET | .NET | Programming

Comments

9/3/2009 12:00:53 AM #

trackback

Take an ASP.NET Application offline with HttpModules

You've been kicked (a good thing) - Trackback from DotNetKicks.com

DotNetKicks.com |

9/3/2009 12:01:01 AM #

trackback

Take an ASP.NET Application offline with HttpModules

You are voted (great) - Trackback from WebDevVote.com

WebDevVote.com |

9/3/2009 12:01:37 AM #

trackback

Take an ASP.NET Application offline with HttpModules | Arnold Matusz's Blog

Thank you for submitting this cool story - Trackback from DotNetShoutout

DotNetShoutout |

9/3/2009 4:29:06 AM #

D0cNet

Nice work. In future it's just a matter of selecting from an admin IP list.;)

D0cNet Jamaica |

9/3/2009 4:39:29 AM #

Arnold Matusz

Implementing some CRUD functionality for Admin IP's was not the scope of this work, although if people request a sample for this, I could sit down and come up with a nice little solution.

Anyway, the whole idea is that anyone can implement their own "authentication" on the sample above. If somebody would like to: he/she could create a filtering based on the existance of specific cookies.

It's up to anyone from here Smile.

Arnold Matusz Romania |

9/3/2009 5:52:17 AM #

Stuart

Have you considered the performance implication of doing an IO read with every request.

Your idea is a good one, although I would probably come up with a different mechanism (other than the IO read) to send visitors to the offline page.

An admin page that toggles an Application variable could be more performant. The admin page could also set the admin IP based on your current IP while accessing the page or you could modify this functionality to be sessionID based.

Stuart United States |

9/3/2009 8:15:18 AM #

Arnold Matusz

As mentioned in the post, this solution works well in a small maintenance group.

The actual IO read overhead is not a problem in an small application used simultaneously by 100~200 users. Of course I have thought about performance, and for bigger number of users I'ld suggest something different than your solution.

Storing an IP address in the Session is not accetable at all due to the fact that this would enable on 1 administrator. (Once the application is down, only one admin can access the website -> and what happens if the session expires?)

I'd rather create an interface in the admin area of the website where you can "enable" multiple IP addresses which are saved to some sort of datastore (file, database, whatever), and of course a big red button with "Take Application Offline" written on it. While these options are saved in the datastore, I'd save these into an Application variable | cache on each Application_Start event.

Then on every request I'd only retrieve the IP addresses from Application["AdminIPAddresses"] (ex).

Anyway, the whole idea of the post remains the same, but performance improvements can always be considered.

Arnold Matusz Romania |

9/3/2009 7:24:57 AM #

tony testa

Good post.  I am about to do something on a project and remembered the app_offline.htm, but didn't really think of the downsides that you brought up in this article.

tony testa United States |

9/3/2009 8:22:54 AM #

Arnold Matusz

Yep, interesting enough but very logical!

Well, if you only need to upload a new version of the website, if you temporarily update your database, or whatsoever, you'll be ok with app_offline.htm but if you need access to the website it won't be good Laughing.

I hope this article helps you.

Arnold Matusz Romania |

9/3/2009 4:27:24 PM #

Nathan

I agree, this is a great idea.  Using a technique to read the web.config would probably be less resource intensive than I/O, but like you said, it's the idea worth noting.

Expanding on your idea... have an application offline section in the admin area of your site.  The administrator sets the reason for taking the application offline and clicks the "take offline" button.  The application updates a application offline specific twitter account with the status (they can update as needed) and updates the web.config with the admin's ip address.  The offline.htm file displays the twitter feed and any other relevant information to ease the pain for the end user.  You can read more about my offline file idea here.  blog.ntierdesign.com/.../creating-a-user-friendly-app-offline-system-maintenance-page

What do you think?

Thanks again for the idea.

-Nathan

Nathan United States |

9/3/2009 6:35:23 PM #

Arnold Matusz

Hey

Thanks for your comment, your idea is practically similar to mine except the client side enhancements. This video thing well suited on a website "out there", but I guess you wouldn't want to do this when the application in question is an intranet application of the company you work for. Smile

Regarding "the offline state in the web.config file", I do not agree with that. Once the file is saved on the production server, the ASP.NET application is restarted which is again a bit wasteful from performance point of view (because then we have to load up everything again). And there is another interesting fact which is more noticeable if the uploaded application has not been precompiled + you save a new web.config file, there will be a little outage until the system recompiles everything and generates the needed dll, into the ASP.NET Temporary Files directory. Depending the server this can vary between just a few seconds right up to ALLOT (based on how big you solution is Laughing).

Thanks for dropping by.

Arnold Matusz Romania |

9/4/2009 3:33:56 AM #

Nathan

True, not for every situation, but the video is just an example.  The ping functionality is the idea.

The web.config isn't optimal for my situation either, but you're most likely taking the application offline to push out some updates right?  Modifying the web.config AND anything in the bin directory will recycle your app pool regardless.  If your concerned about the perf hit, you'll of course want to pre-compile your app prior to publishing.

There's never a one solution that fits all situations, but your post has spun off several ideas for me and for that, I thank you!

Nathan United States |

9/4/2009 3:36:48 AM #

Arnold Matusz

Hey

I simply love this, people are gathering, sharing ideas, and if one, ONLY ONE person will benefit from these ideas, it is all worth it.

Thank you all.

Arnold Matusz Romania |

9/3/2009 8:30:58 PM #

Joakim Eriksson

You can also simply use the IP lock feature in IIS. Then you set the custom error page for http error 403 (IIS7) 403.6 (IIS6) to your html file and you have this functionality without any performance penalty. You can also now do whatever you want with the site (ie mess up the web.config the message will still be shown) and as a bonus you will be using the IIS gui for the configuration.

Joakim Eriksson Sweden |

9/3/2009 8:38:44 PM #

Arnold Matusz

Yes, this is well worth noting, although it's not the solution for websites living in a shared hosting environment.

Arnold Matusz Romania |

9/3/2009 11:15:32 PM #

trackback

Take an ASP.NET Application offline with HttpModules

DotNetBurner - burning hot .net content

DotNetBurner - ASP.net |

9/6/2009 1:03:39 PM #

pingback

Pingback from grantpalin.com

Weekly Links #69 | GrantPalin.com

grantpalin.com |

9/6/2009 7:39:06 PM #

pingback

Pingback from jasper22.wordpress.com

Take an ASP.NET Application offline with HttpModules « Jasper Blog

jasper22.wordpress.com |

9/15/2009 1:05:13 PM #

Chris Cyvas

Thanks for this. I modified it a bit, and thought you might be interested. I tied off the PostAuthenticateRequest and then I was able to use the following logic:

      if (siteSettings.IsStoreClosed && !context.User.IsInRole("Administrator") && !context.Request.Url.ToString().Contains("login.aspx")) {
        context.RewritePath("~/storeclosed.aspx");
      }

This way, even if the administrator logs out, they can still get to the login page to login. A user that logs in can only login and logout - so they are restricted to getting anywhere else other than the login screen.

Thanks - good idea!

Chris Cyvas United States |

9/15/2009 6:29:10 PM #

Arnold Matusz

Hi

Thanks for your comment. Your idea also rocks, and sa said, the whole thing is the HttpModules, from there on ... the possibilities are ~= limitless Smile.

Have a nice day.

Arnold Matusz Romania |

9/17/2009 6:10:30 AM #

pingback

Pingback from iulitab.co.uk

Take an ASP.NET Application offline with HttpModules  - Iulian Tabără: Blog

iulitab.co.uk |

9/28/2009 10:54:04 AM #

pingback

Pingback from zabet.ir

آفلاین کردن یک  ÙˆØ¨ سایت با HttpModules

zabet.ir |