Temporarily redirect *all* HTTP/HTTPS requests in IIS to a "server maintenance" page

26,893

Solution 1

I would go with your third approach "We're offline" Site in IIS, say you named it Offline, if it has no host header specified it will serve all requests not picked up by any of the other sites which have a matching host header. To prevent this you just stop all other sites.

Assuming you have IIS Scripting installed, open an elevated PowerShell:

import-module webadministration

now you can stop all sites except the Offline one:

Get-ChildItem IIS:\Sites | Where {$_.Name -ne "Offline"} | Stop-WebSite

when the SQL-Server is back up, start them up again:

Get-ChildItem IIS:\Sites | Where {$_.Name -ne "Offline"} | Start-WebSite

If you have FTP sites as well, the commands will show an error because you can not pipe an FTP site to a Stop-WebSite cmdlet, but it still works for all the web sites.

If you have sites that normally not run, you have to exclude them in the second command, like:

Where {$_.Name -ne "Offline" -and $_.Name -ne "foobar.com"}

If you don't have the PowerShell cmdlets for IIS installed, you can use appcmd.exe to do the same, I haven't used that in years though.

Solution 2

All our sites are setup in IIS with a single IP.

1) take an old desktop, run live linux distro, give it the same ip as the IIS box, do not connect it to network

2) fire up nginx on live linux box, and make the downtime page as you like it, test it by using an offline switch/hub connected to ur laptop

3) unplug IIS box ethernet cable, and plug it into live linux box.

4) clear mac addr cache on switch (or roll power). your downtime site is now live.

Solution 3

I know this is old, but I just had to do this on an old Windows 2008 r2 box. This is an answer more for the question title; regarding the question detail, it is then simply one approach to setting up a "We're Offline site in IIS".

This doesn't rely on anything more than IIS and static HTML. IIS's "HTTP Redirect" functionality doesn't handle what you want, but there's another way to simulate it. Just change all of the "Error Pages" for the site to point to the maintenance page. Yes, this works only if you can use an entire "site" in IIS.

In my case, the site has a single "default.htm" file in its root folder (e.g. c:\InetPub\wwwroot). So all of the "Error Pages" are configured to "Execute a URL at this site" and use the path "/default.htm". Since I use absolute URLs (i.e. starting with "/") in the file, its content executes correctly in the browser, no matter what the public URL appears to be.

The net result of this configuration is any/all requests to the site serve my maintenance page's content. It doesn't matter what the request is.

Also, be aware that IIS will affect this change by generating a web.config file in the root folder. This is what it created for me:

 <?xml version="1.0" encoding="UTF-8"?>
 <configuration>
     <system.webServer>
         <httpErrors>
             <remove statusCode="502" subStatusCode="-1" />
             <remove statusCode="501" subStatusCode="-1" />
             <remove statusCode="500" subStatusCode="-1" />
             <remove statusCode="412" subStatusCode="-1" />
             <remove statusCode="406" subStatusCode="-1" />
             <remove statusCode="405" subStatusCode="-1" />
             <remove statusCode="404" subStatusCode="-1" />
             <remove statusCode="403" subStatusCode="-1" />
             <remove statusCode="401" subStatusCode="-1" />
             <error statusCode="401" prefixLanguageFilePath="" path="/default.htm" responseMode="ExecuteURL" />
             <error statusCode="403" prefixLanguageFilePath="" path="/default.htm" responseMode="ExecuteURL" />
             <error statusCode="404" prefixLanguageFilePath="" path="/default.htm" responseMode="ExecuteURL" />
             <error statusCode="405" prefixLanguageFilePath="" path="/default.htm" responseMode="ExecuteURL" />
             <error statusCode="406" prefixLanguageFilePath="" path="/default.htm" responseMode="ExecuteURL" />
             <error statusCode="412" prefixLanguageFilePath="" path="/default.htm" responseMode="ExecuteURL" />
             <error statusCode="500" prefixLanguageFilePath="" path="/default.htm" responseMode="ExecuteURL" />
             <error statusCode="501" prefixLanguageFilePath="" path="/default.htm" responseMode="ExecuteURL" />
             <error statusCode="502" prefixLanguageFilePath="" path="/default.htm" responseMode="ExecuteURL" />
         </httpErrors>
     </system.webServer>
 </configuration>
Share:
26,893

Related videos on Youtube

Josh
Author by

Josh

Updated on September 18, 2022

Comments

  • Josh
    Josh over 1 year

    We've got an IIS server that hosts hundreds of separate web apps, and the physical database server that hosts these apps is going to be taken offline for maintenance for a brief period (we expect it to take less than 15 minutes).

    During that period, we want to redirect ALL traffic that comes in, for any website, to a "we're currently undergoing maintenance" page.

    I realize I could do this by going to every web app, and setting up an IIS rewrite rule that sends the user to another page for all requests in that app. But, it would take us longer to do that than it will to do the database maintenance!

    I've tried three things, none of which have worked:

    Global IIS Rewrite Rule

    I've been searching for a simple way to apply a rule to all sites, in one fell swoop--and then be able to "undo" that rule in one equally painless step. So far, none of my attempts have worked. I did try putting this rewrite rule in my global web.config at W:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\web.config:

    <configuration>
        <system.webServer>
            <rewrite>
                <rules>
                    <rule name="redirect all requests" stopProcessing="true">
                        <match url="^(.*)$" ignoreCase="false" />
                        <conditions logicalGrouping="MatchAll">
                            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" pattern="" ignoreCase="false" />
                        </conditions>
                        <action type="Redirect" url="http://www.somedomain.com/maintenance" appendQueryString="true" />
                    </rule>
                </rules>
            </rewrite>
        </system.webServer>
    </configuration>
    

    This didn't work. We're running .NET 4.0 64 bit in IIS, but "just in case" I put the same thing the 32 bit and 2.0 global web.config files, and still no change.

    App_Offline.htm "special file"

    One other suggestion I've seen is the app_offline.htm "special" file, but we're back the same issue of it taking longer to deploy this file to the app root of all our apps than it would to actually do the maintenance.

    "We're offline" Site in IIS

    All our sites are setup in IIS with a single IP. This works for us even without SNA because all our apps share a single SSL certificate (it's a UCC). One thing that occurred to me was that perhaps I could setup a site in IIS that matched all traffic to the IP we're using, and did not specify a host header value. The hope was that I could give it a higher "precedence" and that it, when started, would match all traffic to that IP, before any of the other sites had a chance to match. I could set that site up to serve the same page for all requests, regardless of the request URL.

    Start that site when undergoing maintenance, and stop it when finished.

    But, I wasn't able to get this working either, as IIS seems to match an HTTP request to a more specific site before less specific. So, by omitting a host-header value for this "tell users we're offline" site, it didn't get matched unless the request didn't have a host-header value that matched another site. Which puts us back at the same problem of having to manually go to each web app and perform an action to take it offline and then put it back online when we're done with maintenance

    Is there a simple way to accomplish this? It would seem that surely we are not the first to encounter this issue.

    -Josh

    • phoebus
      phoebus over 10 years
      One option would be to install an instance of apache or another web server and set up your sorry site in it. Then, when the time comes, stop IIS and start apache and have it handle all of the requests.
    • phoebus
      phoebus over 10 years
      The way we commonly do this btw is to have a sorry page on a device in front of the server, e.g. a load balancer.