Decoding HTTP 500.30 on Azure App Service (ASP.NET Core startup failure)
A fresh deploy, a green pipeline, and then a stark grey page: HTTP Error 500.30 — ASP.NET Core app failed to start. It tells you almost nothing on purpose. The good news: the real error is a two-minute dig away — once you know it's a startup failure and where the module hid the exception.
First, scope it. 500.30 is an ASP.NET Core error. It's raised by the ASP.NET Core Module (ANCM) under the in-process hosting model — the default for ASP.NET Core on Windows App Service. It does not come from a classic ASP.NET Framework app. So if you arrived here from the default-hostname redirect problem, note that these are two different app classes, not two symptoms of one bug — just two of the most common ways an App Service faceplants.
What 500.30 actually means
Per Microsoft's own wording, 500.30 is an In-Process Startup Failure: the module "attempts to start the .NET CLR in-process, but it fails to start." Translate that: ANCM successfully launched your app's process, and then your app crashed during startup — it threw before it could begin serving requests. The process came up; the app didn't. That distinction is the whole diagnosis, because it points you at Program.cs / Startup and your boot-time configuration, not at IIS or the network.
The 500.3x family all mean "the module couldn't get your app serving," but each sub-status points somewhere specific. Read the exact number before you start fixing:
- 500.30 — In-Process Startup Failure. Process launched, app threw during startup. Your code / config.
- 500.31 — Failed to Find Native Dependencies. The targeted runtime (
Microsoft.NETCore.App/Microsoft.AspNetCore.App) isn't installed on the machine — a framework/version mismatch. - 500.32 — Failed to Load dll. Processor-architecture mismatch, e.g. a 32-bit worker process running a 64-bit-published app.
- 500.33 — Request Handler Load Failure. The app doesn't reference the
Microsoft.AspNetCore.Appframework. - 500.34 / 500.35 — Mixed / multiple hosting models in one process. Split them into separate app pools.
- 500.37 — Failed to start within the startup time limit (120s by default) — often resource contention.
- 500.38 — Application DLL not found — hosting a single-file executable under the in-process model.
If you're on .31, you have a runtime problem; on .30, you have a crash. This post is about the crash.
The usual root causes
A 500.30 is almost always one of these, roughly in order of how often they bite:
- An exception in
Program.cs/Startup. A bad DI registration, a service that throws in its constructor, or anIHostedServicethat throws inStartAsync— anything on the boot path takes the whole app down. - Missing or mismatched runtime / target framework. The App Service is pinned to a .NET version that doesn't match what you published against. (When the runtime is outright absent you'll usually get
500.31, but a subtle mismatch can surface as a startup crash.) - A config value that's required at boot but absent. A connection string or app setting your startup reads eagerly — present locally, never set in App Service configuration — so the app throws the moment it looks for it.
- A failed database migration on startup. If you run
Migrate()during boot and the database is unreachable or the migration fails, startup fails with it. - A bad
web.config/ ANCM configuration. A wrongprocessPath,arguments, or hosting model in the generatedweb.config. - Key Vault access. Microsoft calls this one out specifically: if startup pulls secrets from Azure Key Vault and the app's identity lacks permission, it fails to start. Check the Key Vault access policies / RBAC.
How to actually diagnose it (this is the whole game)
The 500.30 page is deliberately generic — surfacing stack traces to the public internet would be a security problem. Your job is to make the app tell you what it swallowed. In rough order of speed:
1. Turn on stdout logging in web.config
The fastest path to the real exception. In the <aspNetCore> element of your deployed web.config (edit it live in Kudu — Advanced Tools → Go → Debug console → CMD → site\wwwroot), flip stdout logging on:
<aspNetCore processPath="dotnet" arguments=".\YourApp.dll" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
Reproduce the request, then read the newest file under \logs — turn it back OFF afterwards
Hit the site once to trigger the crash, then open the newest file under site\wwwroot\logs. The unhandled startup exception — the actual message and stack — is written there. Turn stdoutLogEnabled back to false when you're done: left on, it writes forever and can fill the file system.
2. Watch Log stream and Diagnose and solve problems
In the portal, Monitoring → Log stream tails application logs in near real time — reproduce the request and watch the exception scroll by. And Diagnose and solve problems → Application Events surfaces the same failures with Azure's own detectors, often naming the culprit for you.
3. Read the Application Event Log through Kudu
ANCM writes startup failures to the Windows Application Event Log, under sources IIS AspNetCore Module / IIS AspNetCoreModule V2. You can read it from Kudu. For a deeper trace, ANCM can also emit a debug log via <handlerSettings> (debugLevel = file, debugFile = a path) if you need to see the module's own view of the launch.
4. Run the app by hand in Kudu
The bluntest, most reliable move: execute the published app exactly as the module does, and read whatever it prints. In the Kudu CMD console:
cd D:\home\site\wwwroot dotnet .\YourApp.dll
The unhandled exception prints straight to the console — no log plumbing required
If it crashes on startup, the exception lands right in front of you. This is the same thing you can do locally against your published output — see Prevention below.
500.30 isn't the error. It's the envelope the error came in. Every step above is just steaming it open.
Fixing it, by cause
- Startup exception: the stdout log names the type and line. Fix the DI registration, guard the constructor, or move risky work out of the boot path.
- Runtime / framework mismatch: pin the App Service to the .NET version you published against (or republish against what's installed). A true "runtime not found" is
500.31. - Missing config: add the connection string / app setting in Configuration, then restart. Read config through
IConfigurationwith an explicit failure if it's absent (next section). - Failed migration: make the database reachable and the migration valid — or stop migrating on startup and run migrations as a deploy step instead.
- Bad web.config / ANCM: confirm
processPath,arguments, andhostingModelmatch your published DLL. Regenerating from a clean publish usually fixes it. - Key Vault: grant the app's managed identity read access to the vault's secrets.
Preventing the next one
- Pin the runtime. Set the App Service's .NET version explicitly and keep it in step with your target framework, so a platform update never surprises you.
- Validate config at startup, loudly. Read required settings early and throw a clear message ("connection string 'Sql' is not configured") instead of letting a
NullReferenceExceptionthree layers down masquerade as a mystery 500.30. - Test the published output locally. Run
dotnet YourApp.dllagainst your published folder — notdotnet run— before you deploy. Most 500.30s reproduce instantly there, on your machine, where the stack trace is free.
This is the ASP.NET Core half of "App Service is misbehaving." Its classic-ASP.NET counterpart is the one where the platform's *.azurewebsites.net address keeps leaking your app: The Azure App Service default hostname problem (and the web.config fix). Two common App Service faceplants — one a startup crash, one a stray front door.