Skip to content

Windows IIS web.config Startup Failures (Azure App Service Windows)

1. Summary

Symptom

After deployment, the Windows App Service site fails during startup or serves only 500-class failures from IIS. Common signatures include HTTP 500.19, 500.21, 502.5, and HRESULTs like 0x8007000d in startup traces.

Why this scenario is confusing

web.config is evaluated by IIS before your application code is fully available. A single XML, handler, module, rewrite, or locked-setting mismatch can surface as an app outage even though deployment succeeded and binaries are present. Teams often misread this as an application regression instead of an IIS pipeline/configuration failure.

Troubleshooting decision flow (mermaid diagram)

flowchart TD
    A[Startup failure on Windows App Service] --> B{Primary HTTP signature?}
    B -->|500.19 or 0x8007000d| H1[H1: Malformed web.config XML]
    B -->|500.21| H3[H3: Missing runtime or IIS module]
    B -->|502.5| H2[H2: Incorrect processPath or handler mapping]
    B -->|500 with rewrite traces| H4[H4: URL Rewrite rule conflict]
    B -->|Intermittent 500.19 with lock messages| H5[H5: Locked setting conflicts]

    H1 --> C{XML valid and deploy complete?}
    C -->|No| F1[Fix malformed XML and redeploy]
    C -->|Yes| D[Continue hypothesis checks]

    H2 --> E{processPath and arguments valid?}
    E -->|No| F2[Correct AspNetCore/handler config]
    E -->|Yes| D

    H3 --> G{Required module/runtime installed?}
    G -->|No| F3[Install hosting bundle or fix module dependency]
    G -->|Yes| D

    H4 --> I{Rewrite loops or invalid target?}
    I -->|Yes| F4[Disable/fix rewrite rule]
    I -->|No| D

    H5 --> J{Locked section overridden in web.config?}
    J -->|Yes| F5[Remove locked settings and use platform config]
    J -->|No| D

    D --> Z[Validate with HTTP sub-status and startup logs]

Limitations

  • This playbook is only for native Windows App Service running IIS-managed workloads.
  • It does not cover Linux, custom containers, or Kubernetes-hosted runtimes.
  • Framework-level logic bugs after successful IIS startup are out of scope.

Quick Conclusion

Classify first by IIS sub-status code, then verify whether failure is XML parse, handler/process bootstrap, missing module/runtime, rewrite conflict, or locked-setting precedence. For fast recovery, revert to a known-good web.config, remove risky overrides, and confirm stable startup with AppServiceHTTPLogs sub-status trends.

2. Common Misreadings

  • "Deployment passed so startup is healthy" (deployment success does not validate IIS runtime configuration).
  • "500 means app code threw" (for startup incidents, IIS often fails before application code executes).
  • "502.5 is networking" (502.5 is commonly ASP.NET Core process startup failure behind IIS).
  • "500.21 means URL Rewrite syntax" (500.21 usually indicates missing/invalid module registration).
  • "If local IIS works, App Service must behave the same" (platform-level applicationHost.config locks and module availability can differ).

3. Competing Hypotheses

  • H1: Malformed web.config XML (parse error) causes IIS to fail config load (500.19, 0x8007000d).
  • H2: Incorrect processPath or handler mapping prevents ASP.NET Core bootstrap (502.5 or startup crash signatures).
  • H3: Missing runtime/module dependency (for example URL Rewrite module or ASP.NET Core hosting bundle mismatch) triggers module/handler failures (500.21).
  • H4: URL Rewrite rule conflict causes startup-time request failures or redirect/rewriting loops yielding 500.xx.
  • H5: web.config locked settings conflict with App Service applicationHost.config precedence, producing configuration load failures (500.19, section lock messages).

4. What to Check First

Metrics

  • Availability drop aligned with deployment or configuration change timestamp.
  • Surge of 5xx requests and failed requests in App Service HTTP metrics.
  • Restart spikes for the worker process after config updates.

Logs

  • AppServiceHTTPLogs: confirm ScStatus + ScSubStatus signatures (500.19, 500.21, 502.5).
  • Application/Event pipeline logs via Log Stream and Kudu diagnostics.
  • Windows Event Log snippets for IIS/ASP.NET Core module startup errors.

Platform Signals

  • Current web.config content and most recent deployment artifact version.
  • App stack/runtime configuration in App Service (.NET version and bitness where applicable).
  • Effective site-level behavior when applicationHost.config restrictions apply.

Portal view: Logs blade for KQL investigation of IIS sub-status patterns

Azure portal Logs blade for ai-test-20251107 (Application Insights) with a New Query 1 tab open, top-right controls Observability agent (New), Save, Share, Queries hub, and an inline toolbar Run + Time range: Last 24 hours + Show: 1000 results + KQL mode dropdown. The query editor shows placeholder text "Type your query here or click one of the queries to start" on line 1. Below the editor a Query history pane reads "No queries history — You haven't run any queries yet. To start, go to Queries on the side pane or type a query in the query editor." Left nav under Monitoring lists Alerts, Metrics, Diagnostic settings, Logs (selected), Workbooks, Dashboards with Grafana; the Investigate group above is collapsed.

The Logs blade with a New Query 1 tab is the execution surface for KQL queries in the Portal. The visible inline toolbar — Run, Time range: Last 24 hours, Show: 1000 results, KQL mode — names the controls used to bound a query to the incident window. The left-nav Monitoring group lists Alerts, Metrics, Diagnostic settings, Logs (selected), Workbooks, and Dashboards with Grafana — the adjacent surfaces this playbook's evidence section also references. The source resource for this capture is an Application Insights workspace connected to a Linux App Service; the Logs blade itself is OS-agnostic and is the equivalent Portal destination for running this playbook's IIS sub-status queries against a Windows App Service.

5. Evidence to Collect

Required Evidence

  • The exact failing web.config from deployed wwwroot.
  • AppServiceHTTPLogs rows including status and sub-status in incident window.
  • Kudu log stream and Windows Event Log excerpts during failed startup.
  • Timestamped deployment history and last known-good deployment ID.
  • Current App Service runtime configuration (az webapp config show).

Useful Context

  • Whether failure started immediately after adding rewrite/handler/error sections.
  • Whether startup fails on all instances or only after recycle/scale-out.
  • Whether the same package starts when reverting only web.config.
  • Any manual IIS-oriented changes copied from on-prem docs without App Service validation.
  • Whether httpErrors in web.config masks App Service detailed error pages during incident triage.

CLI Investigation Commands (with expected output)

# App state and host metadata
az webapp show --resource-group <resource-group> --name <app-name> --query "{state:state,enabled:enabled,defaultHostName:defaultHostName}" --output table

# Runtime and site-level configuration snapshot
az webapp config show --resource-group <resource-group> --name <app-name> --query "{windowsFxVersion:windowsFxVersion,netFrameworkVersion:netFrameworkVersion,use32BitWorkerProcess:use32BitWorkerProcess,alwaysOn:alwaysOn}" --output table

# Turn on and inspect web server/application logs if needed
az webapp log config --resource-group <resource-group> --name <app-name> --web-server-logging filesystem --detailed-error-messages true --failed-request-tracing true --application-logging filesystem
az webapp log tail --resource-group <resource-group> --name <app-name>

# Deployment events for startup correlation
az webapp log deployment show --resource-group <resource-group> --name <app-name> --output table

Example Output:

State    Enabled    DefaultHostName
-------  ---------  -------------------------------------------
Running  True       <app-name>.azurewebsites.net

WindowsFxVersion    NetFrameworkVersion    Use32BitWorkerProcess    AlwaysOn
------------------  ---------------------  -----------------------  --------
DOTNET|8.0          v4.0                   False                    True

Time                  Id     Status     Author
-------------------   -----  ---------  ----------------------
2026-04-09T05:42:10Z  1021   Success    ci-pipeline
2026-04-09T06:01:34Z  1022   Success    ci-pipeline

How to Read This

Success in deployment history does not disprove IIS startup failure. Treat deployment and startup as separate checkpoints, then correlate by timestamp.

Sample Log Patterns (with real-looking log output)

Windows Event Log / ANCM (malformed XML and process failures)

[Windows Event Log]
2026-04-09T06:03:11Z  Error  IIS AspNetCore Module V2  EventId=1012
Application '/LM/W3SVC/123456789/ROOT' with physical root 'D:\home\site\wwwroot\' failed to load coreclr.
Exception message:
Could not find a part of the path 'D:\home\site\wwwroot\MyApi.dll'.

2026-04-09T06:03:12Z  Error  IIS-W3SVC-WP  EventId=2307
Configuration file is not well-formed XML.
File: \\?\D:\home\site\wwwroot\web.config
Line: 27
Error: 0x8007000d

IIS HTTP Log pattern (sub-status signatures)

[IIS HTTP Logs]
2026-04-09 06:03:12 GET / 500 19 0 15
2026-04-09 06:03:18 GET / 500 21 0 2
2026-04-09 06:03:24 GET / 502 5 32 101

Kudu diagnostic stream pattern (rewrite and lock hints)

[Kudu / Diagnostic Stream]
2026-04-09T06:05:42.231Z ERROR - Failed to read configuration section 'system.webServer/rewrite/rules'.
2026-04-09T06:05:42.231Z ERROR - This configuration section cannot be used at this path.
2026-04-09T06:05:42.231Z ERROR - Lock violation inherited from higher-level configuration.
2026-04-09T06:05:44.114Z ERROR - Rewrite rule 'ForceHttpsAndCanonicalHost' rewritten URL to itself.

How to Read This

500.19 + 0x8007000d strongly indicates malformed XML or invalid configuration syntax. 500.21 points to module/handler problems. 502.5 usually indicates process bootstrap failure behind ANCM.

KQL Queries with Example Output

Query 1: Detect IIS startup failures by sub-status

// Startup-phase 500/502 failures with sub-status focus
AppServiceHTTPLogs
| where TimeGenerated > ago(6h)
| where ScStatus in (500, 502)
| summarize Requests=count() by ScStatus, ScSubStatus
| order by Requests desc

Example Output:

ScStatus ScSubStatus Requests
500 19 84
500 21 31
502 5 19

How to Read This

Prioritize hypotheses by count. Highest 500.19 volume generally means parse/locked-setting path before module/process debugging.

Query 2: Timeline around deployment and startup failures

// Build timeline for incident window
AppServiceHTTPLogs
| where TimeGenerated between (datetime(2026-04-09 06:00:00) .. datetime(2026-04-09 06:15:00))
| where ScStatus in (500, 502)
| project TimeGenerated, CsMethod, CsUriStem, ScStatus, ScSubStatus, ScWin32Status, TimeTaken
| order by TimeGenerated asc

Example Output:

TimeGenerated CsMethod CsUriStem ScStatus ScSubStatus ScWin32Status TimeTaken
2026-04-09 06:03:12 GET / 500 19 0 15
2026-04-09 06:03:18 GET / 500 21 0 2
2026-04-09 06:03:24 GET / 502 5 32 101

6. Validation and Disproof by Hypothesis

H1: Malformed web.config XML (parse error)

Evidence FOR

  • IIS/Event logs show 0x8007000d, line/position parse errors, or malformed XML messages.
  • AppServiceHTTPLogs dominated by 500.19 immediately after deployment.
  • Site fails before application-specific startup logs appear.

Evidence AGAINST

  • XML validates cleanly and no line-level parse errors are present.
  • Failures are primarily 500.21 or 502.5 with module/process signatures.

CLI/KQL to test

# Download and inspect deployed web.config from Kudu (manual check in SCM site)
az webapp deployment list-publishing-profiles --resource-group <resource-group> --name <app-name>

# Reconfirm startup failure signature in live logs
az webapp log tail --resource-group <resource-group> --name <app-name>
AppServiceHTTPLogs
| where TimeGenerated > ago(2h)
| where ScStatus == 500 and ScSubStatus == 19
| project TimeGenerated, CsUriStem, ScStatus, ScSubStatus, ScWin32Status
| order by TimeGenerated desc

Expected results for normal vs abnormal

State Expected
Normal No sustained 500.19; startup reaches 200/302 shortly after recycle.
Abnormal Repeated 500.19; parse/format error hints (including 0x8007000d) in logs.

H2: Incorrect processPath or handler mapping

Evidence FOR

  • 502.5 appears with ANCM startup errors indicating target executable or DLL path mismatch.
  • aspNetCore processPath points to wrong file (for example stale DLL name).
  • Handler mapping references unsupported/incorrect module declaration for current app stack.

Evidence AGAINST

  • Process path exists and launches correctly under current deployment output.
  • No ANCM startup exceptions; failures center on rewrite or locked sections.

CLI/KQL to test

# Verify runtime stack and startup-relevant configuration
az webapp config show --resource-group <resource-group> --name <app-name> --query "{windowsFxVersion:windowsFxVersion,netFrameworkVersion:netFrameworkVersion}" --output table

# Stream logs while recycling to catch process bootstrap errors
az webapp restart --resource-group <resource-group> --name <app-name>
az webapp log tail --resource-group <resource-group> --name <app-name>
AppServiceHTTPLogs
| where TimeGenerated > ago(2h)
| where ScStatus == 502 and ScSubStatus == 5
| summarize Requests=count(), FirstSeen=min(TimeGenerated), LastSeen=max(TimeGenerated)

Expected results for normal vs abnormal

State Expected
Normal 502.5 absent; startup logs show process launched and responding.
Abnormal 502.5 spikes after recycle; ANCM errors report invalid executable/DLL path or bootstrap failure.

H3: Missing runtime/module dependency

Evidence FOR

  • 500.21 responses indicating unrecognized module/handler configuration.
  • Handler config references HttpPlatformHandler or another module not available for the deployed app/runtime model.
  • Logs indicate missing URL Rewrite module or ASP.NET Core hosting bundle/runtime mismatch.
  • Existing web.config works in one environment but fails in App Service due to unavailable module assumptions.

Evidence AGAINST

  • Required runtime and modules are present and matched to app target framework.
  • No module resolution errors appear in IIS/Kudu diagnostics.

CLI/KQL to test

# Capture effective site configuration and logging for module diagnostics
az webapp config show --resource-group <resource-group> --name <app-name> --output json
az webapp log config --resource-group <resource-group> --name <app-name> --web-server-logging filesystem --failed-request-tracing true --detailed-error-messages true --application-logging filesystem
az webapp log tail --resource-group <resource-group> --name <app-name>
AppServiceHTTPLogs
| where TimeGenerated > ago(2h)
| where ScStatus == 500 and ScSubStatus == 21
| project TimeGenerated, CsMethod, CsUriStem, ScStatus, ScSubStatus, ScWin32Status
| order by TimeGenerated desc

Expected results for normal vs abnormal

State Expected
Normal No 500.21 trend; routes return app-generated responses.
Abnormal Persistent 500.21; diagnostics show module not recognized or dependency absent.

H4: URL Rewrite rule conflict

Evidence FOR

  • Startup or first requests produce 500.xx with rewrite evaluation errors.
  • Kudu stream shows rewrite loop, invalid back-reference, or destination path conflict.
  • Recent rewrite updates (HTTPS/host canonicalization rules) coincide with outage start.

Evidence AGAINST

  • Rewrite section disabled/removed and failure signature unchanged.
  • 500.19 parse errors exist before rewrite processing.

CLI/KQL to test

# Reproduce quickly after restart and watch diagnostics
az webapp restart --resource-group <resource-group> --name <app-name>
az webapp log tail --resource-group <resource-group> --name <app-name>
AppServiceHTTPLogs
| where TimeGenerated > ago(2h)
| where ScStatus == 500
| summarize Requests=count() by ScSubStatus
| order by Requests desc

Expected results for normal vs abnormal

State Expected
Normal Rewrite rules resolve once per request with no loop/error signals.
Abnormal Repeated startup/initial-request 500 with rewrite loop or rule parse diagnostics.

H5: web.config locked settings conflict with applicationHost.config

Evidence FOR

  • Error states configuration section cannot be used at this path or is locked at parent level.
  • 500.19 appears when web.config attempts to override locked system.webServer sections.
  • Incident starts after adding on-prem IIS settings not permitted in App Service tenant-level config.

Evidence AGAINST

  • No lock violation messages in diagnostics.
  • Removing suspect locked sections has no impact on failure pattern.

CLI/KQL to test

# Capture current site settings, then tail for lock violations during restart
az webapp config show --resource-group <resource-group> --name <app-name> --output table
az webapp restart --resource-group <resource-group> --name <app-name>
az webapp log tail --resource-group <resource-group> --name <app-name>
AppServiceHTTPLogs
| where TimeGenerated > ago(2h)
| where ScStatus == 500 and ScSubStatus == 19
| summarize Requests=count(), Win32StatusSet=make_set(ScWin32Status, 5)

Expected results for normal vs abnormal

State Expected
Normal No lock violations; site-level config accepted and app starts.
Abnormal 500.19 persists with lock/path violation messages tied to system.webServer overrides.

Normal vs Abnormal Comparison

Signal Normal Startup IIS web.config Startup Failure
HTTP status mix Mostly 200/301/302 after warm-up Sustained 500.19, 500.21, or 502.5
Sub-status trend No stable 500 sub-status cluster One or more dominant sub-status signatures
Event/Kudu diagnostics No parse/module/lock errors XML parse, missing module, rewrite loop, or locked section errors
Startup progression Worker reaches ready state quickly IIS blocks request pipeline before stable readiness
Recovery action Standard restart succeeds Requires config correction or rollback

7. Likely Root Cause Patterns

  • Pattern A: Manual web.config edits introduced malformed XML, invalid escaping, or unclosed elements.
  • Pattern B: ASP.NET Core deployment changed assembly name/output path but processPath/arguments remained stale.
  • Pattern C: web.config includes module declarations unsupported in the active App Service Windows environment.
  • Pattern D: Rewrite rule migrated from on-prem IIS creates self-rewrite or conflicting redirect logic.
  • Pattern E: Locked configuration sections are overridden in web.config even though parent-level applicationHost.config disallows them.
  • Pattern F: httpErrors customization hides useful diagnostics and masks platform-generated error behavior during incident response.

8. Immediate Mitigations

  • Roll back to last known-good deployment that includes a previously working web.config.
  • Temporarily remove non-essential rewrite, custom handlers, and aggressive httpErrors overrides to restore startup.
  • Correct ASP.NET Core aspNetCore processPath and arguments to match current deployed artifacts.
  • Remove or simplify module registrations until 500.21 is eliminated.
  • If lock violations appear, delete conflicting sections and rely on App Service-supported configuration.

Production Safety

Apply one mitigation at a time and validate with sub-status trends. Bundling multiple edits makes root cause confirmation harder and increases rollback risk.

9. Prevention

  • Add CI validation for web.config XML schema and well-formedness before deployment.
  • Keep web.config minimal; avoid carrying full on-prem IIS templates into App Service Windows.
  • Validate rewrite rules with loop-safe conditions and explicit exclusions.
  • Version-control and review any change to handlers, modules, and aspNetCore processPath.
  • Avoid overriding httpErrors unless explicitly required, and document impact on troubleshooting visibility.
  • Maintain a startup smoke test that checks first-request success and alerts on 500.19, 500.21, and 502.5 spikes.

See Also

Sources