Handling website maintenance in IIS
I’m the proud owner of a few websites, all of which run from an IIS 8.5 server. I also help friends and family run a few sites from the same server and some are business sites that, ideally, shouldn’t be offline for long periods of time. From time-to-time, that server needs a reboot for updates or some other type of maintenance. My friends and family are very understanding that, for the low-low price of free* they occasionally suffer downtime while I patch the server and give it a bounce so they suffer a little website maintenance message.
With some of the websites I help host being business sites, there’s half a chance that a search engine is crawling the site when I’m merrily going about my patching. Having a site become completely unresponsive, or worse, sending a 404 isn’t good for search rankings so it makes sense to use the best solution for dealing with search engines while still being informative for users as well.
While a site is temporarily unavailable, it is best to send an HTTP 503 Service Unavailable status code. The HTTP 503 Service Unavailable indicates that the service is temporarily unavailable and that the client should retry its request. I should add that, ideally the response should include a Retry-After header also but this isn’t straightforward to do without some intervening code, either from ASP.NET or PHP to send that particular header in the response along with the HTTP 503 Service Unavailable .
The purpose of sending an HTTP 503 Service Unavailable is primarily for search engines – your users can happily read and understand a simple “The site is undergoing maintenance.” message but a search engine does not. If a search engine receives an HTTP 200 OK for some content that it’s crawling, your “The site is undergoing maintenance.” message is what’s going to end up in the search index for that request. Probably not what you want.
So, what’s the solution? For me, it involves another server to which I can temporarily direct incoming requests. IIS is installed with the Default Website configured and listening on all IP addresses without any host headers or other sites. URL Rewrite is also installed. TIP: This doesn’t have to be done with a full blown Windows Server installation, it works just as well on a Windows client with IIS or IIS Express installed.
In the website root, I place a web.config file with the following contents:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <rewrite> <rules> <rule name="Site Unavailable" stopProcessing="true"> <match url=".*" /> <conditions> <add input="{REMOTE_ADDR}" pattern="192\.168\.1\.[0-9]{1,3}" negate="true" /> </conditions> <action type="CustomResponse" statusCode="503" subStatusCode="0" statusReason="Site Unavailable" statusDescription="Down for maintenance" /> </rule> </rules> </rewrite> <httpErrors errorMode="Custom"> <error statusCode="503" path="503.htm" responseMode="File" /> </httpErrors> </system.webServer> </configuration> |
The purpose of the directives in the web.config file is to intercept all inbound requests (even for sub-directories, files and images etc.) and respond with an HTTP response code of 503 and a short reason. Those of you paying attention will also see there is a condition there based on REMOTE_ADDR to match any IP address not (the negate=”true” bit) in the 192.168.1.0/24 range. This allows anyone in my subnet to access the website while everyone else gets the 503.
You’ll also notice in the httpErrors section that I have overridden the 503 status code and selected to send back a File called 503.htm which is also placed in the root alongside the web.config.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Maintenance in progress...</title> <link href='http://fonts.googleapis.com/css?family=Lato' rel='stylesheet' type='text/css'> <style type="text/css"> <!-- .center { width: 715px; padding-top: 80px; margin-left: auto; margin-right: auto; } .offline { font-family: "Lato", Arial, Helvetica, sans-serif; font-size: 24px; } .signoff { font-size: 14px; } --> </style> </head> <body> <div class="offline center"> <p>Our website is currently undergoing maintenance.</p> <p>We are working quickly behind the scenes and the site will be available again very shortly.</p> <p class="signoff">Thank you for your patience.</p> </div> </body> </html> |
With these two files in place, anyone sending a request to this IIS server will receive a friendly and informative message. Crucially however, a search crawler will get an HTTP 503 Service Unavailable response and should back off and retry at some point in the future.
I should probably mention that there’s little point putting this on the server that will be getting rebooted. Arguably, there’s no reason why this couldn’t be done on a Windows client workstation with IIS or IIS Express installed, but use whichever is easiest to spin up for you and don’t forget to allow TCP/80 through the firewall on the machine that’s supposed to be listening for requests and sending that 503!
– Lewis
A quick shout out to other resources out there that discuss this same subject.
PS. Here’s a quick site configuration XML snippet you can use with IIS Express that you can drop in to your applicationhost.config (look in %USERPROFILE%\Documents\IISExpress\config). Once you’ve added this XML to your applicationhost.config file, simply drop the two files as above in the location defined in the physicalPath directive and run iisexpress.exe /site:Temp503 from the command line. Obviously you need IIS Express installed.
1 2 3 4 5 6 7 8 9 10 11 |
<sites> ... <site name="Temp503" id="99"> <application path="/" > <virtualDirectory path="/" physicalPath="C:\TempSite" /> </application> <bindings> <binding protocol="http" bindingInformation="*:80:*" /> </bindings> </site> </sites> |
PPS. Don’t forget, if you don’t have another machine configured with IIS that you can use, consider Azure Virtual Machines for the few hours that your sites might be unavailable. Remember, the point of IaaS is to allow you to expand and contract your services as you require so you could configure a hybrid cloud network and extend your network in to Azure before directing the inbound traffic to one of the new Azure VMs running on the Azure side of the hybrid cloud. If however you decided not to create a hybrid cloud and just use a simple Azure VM, you should reduce the TTL values on any DNS records you might alter so that when you switch over and then back from your maintenance pages, the flip takes the least possible time to become effective. These are just thoughts (and not necessarily what I’d recommend) so give each potential solution some consideration first.
Thanks for the post, this is exactly what I need to setup. Quick question if I may…will this work for a PHP site running on a windows machine or would I need to set this up on the htaccess file?
Thanks