When managing an IIS server hosting hundreds of web applications, temporarily redirecting all traffic during maintenance presents unique scaling challenges. Traditional methods like per-application rewrite rules or App_Offline.htm files become impractical due to deployment overhead.
Here's a server-level approach that works without modifying individual applications:
<configuration>
<system.webServer>
<tracing>
<traceFailedRequests>
<add path="*">
<traceAreas>
<add provider="ASP" verbosity="Verbose" />
<add provider="ASPNET" areas="Infrastructure,Module,Page,AppServices" verbosity="Verbose" />
</traceAreas>
<failureDefinitions statusCodes="200-999" />
</add>
</traceFailedRequests>
</tracing>
<rewrite>
<rules>
<rule name="Global Maintenance Redirect" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern="maintenance.example.com" negate="true" />
</conditions>
<action type="Redirect" url="https://maintenance.example.com" appendQueryString="false" redirectType="Temporary" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
1. Create a dedicated maintenance website in IIS
2. Deploy the rewrite rule at server level (applicationHost.config)
3. Use PowerShell to toggle maintenance mode:
# Enable Maintenance Mode
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter "system.webServer/rewrite/rules/rule[@name='Global Maintenance Redirect']/match" -Name "url" -Value "*"
# Disable Maintenance Mode
Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter "system.webServer/rewrite/rules/rule[@name='Global Maintenance Redirect']/match" -Name "url" -Value "disable_maintenance_redirect"
For complex environments, consider these enhancements:
<rule name="Maintenance Bypass for Health Checks" patternSyntax="Wildcard" stopProcessing="true">
<match url="healthcheck.aspx" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{REMOTE_ADDR}" pattern="192\.168\.1\.100" />
</conditions>
<action type="None" />
</rule>
When implementing global redirects:
- Use 307 (Temporary Redirect) status code
- Minimize the maintenance page size (under 10KB)
- Set appropriate cache headers: Cache-Control: no-store, no-cache
When managing multiple web applications on IIS with shared backend resources, temporary maintenance windows present a unique operational challenge. The core problem emerges when you need to:
- Redirect ALL traffic across multiple sites simultaneously
- Implement the solution quickly (faster than the maintenance duration)
- Maintain clean revert capability
- Avoid manual per-site configuration
Option 1: Centralized URL Rewrite at Server Level
Contrary to common documentation, the global applicationHost.config
approach works better than framework-level web.config modifications:
<configuration>
<system.webServer>
<rewrite>
<globalRules>
<rule name="Global Maintenance Redirect" enabled="true" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern="localhost" negate="true" />
</conditions>
<action type="Redirect" url="https://status.yourdomain.com/maintenance"
appendQueryString="false" redirectType="Temporary" />
</rule>
</globalRules>
</rewrite>
</system.webServer>
</configuration>
Key advantages:
- Applies to all sites without individual deployment
- Can be toggled via
appcmd set config
commands - Excludes internal health checks (localhost condition)
Option 2: Failover Binding Technique
For environments using single IP with host headers:
:: PowerShell implementation
Import-Module WebAdministration
# Create maintenance site
New-WebSite -Name "MaintenanceProxy" -Port 80 -IPAddress "*" -PhysicalPath "C:\inetpub\maintenance"
Set-ItemProperty "IIS:\Sites\MaintenanceProxy" -Name Bindings -Value @{protocol="http";bindingInformation="*:80:"}
# Prioritize the site
Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.applicationHost/sites/site[@name='MaintenanceProxy']" -name "applicationDefaults.preloadEnabled" -value "true"
# Alternate method using binding precedence
(Get-WebBinding -Name "MaintenanceProxy").ElementInformation.Properties["bindingInformation"].Value = "*:80:"
For zero-downtime transitions:
@echo off
:: Batch script for maintenance mode toggle
SET MAINTENANCE_MODE=%1
if "%MAINTENANCE_MODE%"=="ON" (
appcmd set config -section:system.webServer/rewrite/globalRules /+"[name='Global Maintenance Redirect',enabled='True']" /commit:apphost
iisreset /noforce
) else (
appcmd set config -section:system.webServer/rewrite/globalRules /-"[name='Global Maintenance Redirect',enabled='True']" /commit:apphost
iisreset /noforce
)
Remember to:
- Test with non-production traffic first
- Implement proper cache-control headers on maintenance page
- Consider HTTP 503 status for API endpoints
- Log all redirect events for post-maintenance analysis