ASP.NET Core HTTP 502 Bad Gateway: Causes and Fixes
HTTP 502 Bad Gateway is one of the most disruptive errors a production ASP.NET Core app can throw — users see a broken page, you see a vague error message, and the actual cause lives somewhere between your application code, the IIS layer, and any reverse-proxy in front. The error number alone narrows the problem to "something upstream failed," but that something could be six different things. This is the structured diagnostic guide to the seven causes of HTTP 502 on ASP.NET Core deployments, ordered by frequency.
What HTTP 502 actually means
The HTTP spec defines 502 Bad Gateway as a response from a server acting as a gateway or proxy when it can't get a valid response from the upstream server. For ASP.NET Core on IIS, the "gateway" is IIS itself (more specifically, the ASP.NET Core Module) and the "upstream" is your application's Kestrel process or in-process runtime. A 502 means IIS made the request to your app and the app either crashed, didn't respond in time, or never came up at all.
The variant codes IIS surfaces help narrow this:
HTTP codeSpecific meaning
HTTP 502.3Bad Gateway — connection broken between IIS and upstream
HTTP 502.5ANCM Out-Of-Process Startup Failure — Kestrel didn't start
HTTP 502 (generic)Bad Gateway from an external reverse proxy or CDN
Read the exact error number first. It cuts the problem space from "anywhere upstream" to "two or three specific causes."
The 7 causes in order of frequency
- Out-of-process Kestrel startup failure (HTTP 502.5)
The most common cause when running ASP.NET Core in hostingModel="outofprocess". The ASP.NET Core Module launches your app's dotnet YourApp.dll (or your AOT-compiled YourApp.exe) as a separate process, then proxies HTTP requests to it. If that Kestrel process fails to start, IIS sees no upstream listener and synthesizes a 502.5.
Common reasons Kestrel fails to start:
Missing .NET runtime (the host doesn't have the version your .csproj targets)
Configuration error (missing appsettings.Production.json, malformed JSON)
Database connection blocking startup (auth fails, server unreachable, startup migration throws)
Required environment variable missing (a key your Program.cs reads eagerly)
Native dependency mismatch (a NuGet package has a Linux-only native binary)
Diagnostic: Enable stdout logging temporarily in web.config:
<aspNetCore processPath="dotnet" arguments=".\YourApp.dll"
stdoutLogEnabled="true"
stdoutLogFile=".\logs\stdout"
hostingModel="outofprocess" />
Restart the app pool, reproduce the 502.5, and check logs/stdout_*.log for the actual .NET exception. Turn stdout logging off once diagnosed — it has performance overhead and fills disk space.
For the related in-process startup failure (HTTP 500.30), see our HTTP 500.30 troubleshooting guide. The diagnostic approach is similar; the error code differs by hosting model.
- Kestrel crashed mid-request
The Kestrel process was running fine when the request arrived, then crashed before responding. IIS reports 502.3 (Bad Gateway, connection broken).
Common reasons:
Unhandled exception in middleware that escapes the global exception handler
Stack overflow (deep recursion, often from misconfigured AutoMapper or reflection over self-referencing types)
Memory exhaustion (worker hit OOM limit and got killed by IIS)
Native code crash inside a NuGet package's interop layer
Diagnostic: Check Windows Event Viewer → Application logs filtered to source = ".NET Runtime" or your app's process name. The crash dump (if AOT or in-process) or stdout log (if out-of-process) records the exception type and stack trace. For repeated crashes, capture a memory dump with procdump -ma w3wp.exe at the moment of crash.
- Request timed out before Kestrel could respond
Your request hit Kestrel, Kestrel started processing it, but it took longer than IIS's upstream-timeout window. IIS gives up waiting and returns 502. Common when:
A long-running synchronous handler (file upload processing, image transformation, ML inference)
A blocking I/O operation waiting on a slow external API
SQL query taking 30+ seconds while EF Core waits
Diagnostic: Look at the timing in IIS request logs. If the 502 happens roughly N seconds after the request arrived, IIS hit its timeout. The default for requestTimeout on the ASP.NET Core Module is 2 minutes. For legitimately long-running operations, increase it in web.config:
<aspNetCore processPath="dotnet" arguments=".\YourApp.dll"
requestTimeout="00:05:00"
hostingModel="outofprocess" />
But raising the timeout is a band-aid. Long-running endpoints should run as background work via IHostedService + a polling endpoint or via Hangfire/Quartz.
- Reverse proxy or CDN can't reach the origin
If your site is behind Cloudflare, AWS Application Load Balancer, an external reverse proxy, or any CDN, the 502 may come from them, not IIS. Cloudflare in particular returns 502 when it can't get a response from the origin server within its window.
Common reasons:
Origin server's IIS is down (app pool stopped)
Origin firewall blocked the CDN's IP range
DNS misconfiguration (the CDN is pointed at the wrong origin)
Origin SSL certificate expired and the CDN's "Full (strict)" SSL mode rejects it
Origin's response exceeded the CDN's body-size limit
Diagnostic: Identify which layer returned the 502 by reading response headers. Cloudflare's 502s include cf-ray: headers and a Cloudflare branding page. AWS ALB returns 502 with specific format codes (visible in CloudWatch logs). If the 502 is from the CDN, IIS logs on the origin won't show the request at all — that tells you the request never reached origin, so the failure is upstream of IIS.
- In-process worker recycled mid-request
For hostingModel="inprocess" apps, if the IIS worker recycles (memory limit hit, time-based recycle, manual recycle) while a request is in flight, that request fails. Depending on timing, it may surface as 502 or 500.30.
Diagnostic: Check Event Viewer for WAS source events — you'll see the recycle event with the reason field (memory limit, idle timeout, configuration change). Adjust app pool settings: raise memory limits if memory pressure is the trigger, disable idle timeout (set to 0) if you don't want suspend-on-idle behaviour.
On Adaptive Web Hosting plans, IIS Application Pool recycle settings are production-tuned by default — no idle timeout, no file-change recycle, long regular-time intervals. App pool recycles mid-request are rare here.
- SSL/TLS handshake failure between IIS and upstream
If you've configured IIS to talk to Kestrel over HTTPS (uncommon for in-process; more common for out-of-process with custom configuration), a TLS handshake mismatch produces a 502. Causes:
The localhost certificate Kestrel uses isn't trusted by IIS
TLS version mismatch (Kestrel rejects TLS 1.0 that IIS attempts)
Self-signed cert without the right trust chain
Most ASP.NET Core deployments use HTTP between IIS and Kestrel for simplicity — the IIS-to-Kestrel hop is on localhost, never crosses the network. If you've explicitly enabled HTTPS between them, verify the cert is in the LocalMachine trust store.
- Worker process never started (configuration error)
If web.config is malformed, references a missing DLL, or has the wrong processPath, the ASP.NET Core Module can't launch the worker at all. The response is typically 500.19 (configuration error) or 502 (gateway error) depending on the specific failure.
Diagnostic: Read the IIS error page detailed information — the line of web.config that broke is named explicitly. Common breakages: typo in DLL filename, wrong relative path, missing closing tag.
The diagnostic flow we recommend
Read the exact error code. 502.5 = startup failure, 502.3 = mid-request, generic 502 = could be CDN. Use the table at top to narrow.
Check IIS logs in Plesk's IIS log viewer (or via FTP at logs/). Look for the failing request — request path, timing, response code.
Enable stdout logging if it's 502.5. Restart pool, reproduce, read the exception from logs/stdout_*.log.
Check Event Viewer Application + System logs filtered to ".NET Runtime" or "WAS". Crash and recycle events live here with full stack traces.
Check if the 502 came from your origin or a CDN by looking at the response headers. Cloudflare 502s include cf-ray; AWS ALB 502s have characteristic body format.
If timing-related, look at how long the request was in flight before the 502 hit. If consistently N seconds, the upstream timeout is N.
Production-ready settings that prevent 502s
For an IIS-hosted ASP.NET Core app, the configuration that minimizes 502 incidents:
In web.config (for out-of-process; in-process is similar)
<aspNetCore processPath="dotnet" arguments=".\YourApp.dll"
stdoutLogEnabled="false"
stdoutLogFile=".\logs\stdout"
hostingModel="outofprocess"
requestTimeout="00:02:00"
startupTimeLimit="120"
shutdownTimeLimit="20" />
startupTimeLimit=120 gives the worker 2 minutes to start — enough for an app that does database migration or warm-up on first launch. shutdownTimeLimit=20 lets graceful shutdown complete; values too short cause IIS to kill the worker while it's still cleaning up.
App pool settings
Idle Time-out: 0 (don't suspend mid-day; cold-start every few minutes is brutal for users)
Regular Time Interval: 0 or ≥24h (don't recycle during business hours)
Memory limits: Generous — rapid recycle on memory pressure causes a 502 storm
Rapid-Fail Protection: Enabled (default); if it's tripping, fix the underlying crash rather than disabling protection
Managed Code Version: No Managed Code (correct for ASP.NET Core; see also our won't-start article for the most common app pool gotcha)
Application code
Wrap startup logic in try/catch and log to stdout before crashing — lets you read the failure reason
Defer optional external service connections from Program.cs startup to first-request lazy initialization
Use async I/O everywhere; never block on .Result or .Wait()
For known-slow operations, run as background IHostedService with a polling status endpoint rather than blocking the request thread
How Adaptive Web Hosting's infrastructure helps
The most common platform-level causes of 502s on shared Windows hosting are missing .NET runtime versions (host lags Microsoft's release cycle) and aggressive app pool recycle settings (defaults from older IIS environments). Adaptive Web Hosting's ASP.NET Core plans address both:
All current LTS runtimes pre-installed — .NET 8 LTS and .NET 10 LTS side-by-side; we add new versions as Microsoft releases them. Runtime-missing 502.5 errors don't happen because the runtime is always there.
Production-tuned default app pool settings — no idle timeout, no file-change recycle, long regular intervals, generous memory ceilings for the plan tier
Dedicated IIS Application Pools per site — your worker process has predictable memory and isn't recycled by neighbour-tenant pressure
Plesk for Windows IIS log viewer — direct access to IIS logs without RDP; stdout log files accessible via the file manager
Real SQL Server 2022 included — database connection failures (a common cause of 502.5) are less likely when the SQL Server is on the same machine, no shared-instance limits, no Express-edition caps
Frequently asked questions
What's the difference between 502 Bad Gateway and 504 Gateway Timeout?
502 means the gateway got an invalid response (or no response at all) from the upstream. 504 specifically means the upstream took too long — the gateway timed out waiting. Some CDNs treat them interchangeably; the underlying meaning differs slightly. For IIS + ASP.NET Core, you'll see 502 more often than 504.
Why does my app work locally with dotnet run but produce 502 on IIS?
dotnet run launches Kestrel directly without the ASP.NET Core Module layer that IIS uses. The 502 typically means the ANCM can't launch your app the same way dotnet run does locally. Common causes: production-only configuration missing, wrong runtime version, file permissions, or app pool "Managed Code" set to .NET CLR v4.0 instead of "No Managed Code." See our won't-start article for the full diagnostic flow.
Should I use in-process or out-of-process hosting to avoid 502s?
In-process is the modern default and avoids the IIS-to-Kestrel hop entirely — one less layer that can fail. For most ASP.NET Core apps, in-process is faster, lower-memory, and fails with HTTP 500.30 instead of 502.5 (slightly easier to diagnose). Out-of-process makes sense for AOT-compiled apps (the .exe contains its own runtime) or when you need IIS modules that don't work in-process.
Does Adaptive Web Hosting see 502s for customers?
Like any host, occasionally — the underlying causes are virtually always application-side (startup config, database unreachable, code throwing). Hosting-side causes (missing runtime, aggressive recycle, neighbour-tenant resource exhaustion) are rare on our plans because runtimes are pre-installed and app pools are dedicated. When customers do hit 502s, the diagnostic flow above resolves the vast majority within 15 minutes once stdout logging is enabled.
What if Cloudflare returns 502 but my origin is up?
Most likely Cloudflare can reach your origin but the response from origin doesn't conform to what Cloudflare expects (response body too large, malformed headers, Cloudflare's "Full (strict)" SSL mode rejecting your certificate). Check Cloudflare's dashboard error log for the specific reason — they distinguish between "origin unreachable," "origin SSL error," "origin returned 5xx," etc.
How do I prevent a single bad endpoint from causing 502s site-wide?
Single-endpoint failures shouldn't bring down the whole site. If one bad route is causing global 502s, either (a) it's exhausting a global resource (connection pool, thread pool, memory) under load, or (b) it's crashing the worker process. Look at whether the 502 affects other endpoints simultaneously or just the one. If just one, isolate it to a background IHostedService or move it to its own subdomain on the same plan.
Should I configure ASP.NET Core's UseHsts middleware to prevent 502s?
HSTS doesn't prevent 502s — it's a security policy header that tells browsers to insist on HTTPS. Different concern. For 502 prevention, focus on the diagnostic flow above plus the production-ready settings section.
Bottom line
HTTP 502 on ASP.NET Core deployments traces to one of seven specific causes: out-of-process Kestrel startup failure, mid-request Kestrel crash, request timeout, CDN/proxy origin unreachable, in-process worker recycle, IIS-to-upstream TLS failure, or web.config configuration error. The exact error code (502.3 vs 502.5 vs generic 502) narrows the list to 1-2 before you even look at logs.
On Adaptive Web Hosting's ASP.NET Core plans, pre-installed runtimes, production-tuned app pool defaults, dedicated app pools, and real SQL Server 2022 eliminate most platform-level causes. The rest is application code or configuration, and the diagnostic flow above resolves them within minutes. Every plan includes a 30-day money-back guarantee. View hosting plans, see our HTTP 500.30 troubleshooting guide for the in-process equivalent, or talk to an ASP.NET expert.