App Service 502 and 503 errors: a diagnosis flow from symptom to fix
App Service 502 and 503 errors nearly always mean the app itself is slow, out of memory or CPU, or crashing on start. This diagnosis flow goes from the first symptom to the fix: check service health, read the metrics, use Diagnose and solve problems and Kudu, then mitigate with scale, auto-heal or a restart.
Apps & integrationFix a problem7 min read
"502 Bad Gateway" and "503 Service Unavailable" are the two errors an Azure App Service app shows when the platform's front end can't get an answer from your application. They look like infrastructure faults, which is why the first instinct is to blame Azure, but Microsoft's own troubleshooting guide is blunt: the cause is usually in the app. Requests take too long, the process is out of memory or CPU, or an exception stops it responding. This guide is the order to work in, so you find the real cause instead of restarting until it goes away.
Step 1: rule out the platform
- Service Health. In the Azure portal, check Service Health for App Service in your region. A platform incident is rare, but it's the one cause you can't fix, so eliminate it first.
- Metrics. On the app, Monitoring > Metrics, and chart the last few hours of: Requests, Average memory working set, Memory working set, CPU time, plus Http Server Errors and Response Time. You're looking for the shape: 5xx rising with CPU or memory (resource pressure), 5xx with response time climbing first (slow requests), or 5xx in a cliff after a deployment or a restart (a crash on start-up).
Set an alert on 5xx while you're there, so next time you hear about it before your users do.
Step 2: collect the evidence
Diagnose and solve problems is the first stop, not the last. Open it from the app's menu and go to Availability and Performance. The Web App Down and Web App Slow detectors analyse the last 24 hours and point at the likely cause; with Application Insights connected (Windows apps), they show the exceptions and failing dependencies at the time of the errors. The Application Changes detector lists configuration and code changes around the outage. Nothing to configure; it works on Windows, Linux, containers and Functions.
Kudu gives you the raw material when the detectors aren't enough. Development Tools > Advanced Tools > Go opens it (https://<app>-<hash>.scm.<region>.azurewebsites.net). Use the log stream to watch the app start, the process explorer for memory per process, the debug console to read files and environment settings, and ProcDump for a memory dump when a first-chance exception is the suspect.
Turn on application logging and web server logging under App Service logs if they're off; a 503 with an empty log is far harder than one with a stack trace.
Step 3: match the symptom to its cause
| What you see | Likely cause | What to do |
|---|---|---|
| 503s under load; response time climbs first; CPU or memory near the plan's limit | Requests take too long, or the plan is too small for the traffic | Profile the slow paths; scale up the plan or out to more instances; move long work to a queue and a worker |
| Memory working set grows until the app restarts, then repeats | A memory leak, or the app genuinely needs more than the tier gives | Take a dump in Kudu and find what holds the memory; scale up while you fix it; set an auto-heal rule on memory as a guard |
| 502 or 503 right after every deployment, then recovery | Slow start-up; the first requests hit a cold process | Deploy to a staging slot, warm it, and swap; turn on Always On (Basic and above) so the app isn't unloaded when idle |
| Every request fails after a deployment; the log stream shows an exception on start | A crash on start-up: a missing setting, a bad connection string, a dependency that fails to load | Fix the setting and redeploy; next time use a slot so a broken build never reaches production |
| A custom container returns 502; the log shows the app listening on a port other than 80 | App Service sends traffic to port 80 by default; your container listens elsewhere | Set the WEBSITES_PORT app setting to the container's port, or make the container listen on 80 |
| A request fails at exactly 230 seconds | App Service's load balancer times requests out at 230 seconds, whatever the app does | Return early and do the work asynchronously; a queue plus a worker, or a Durable Functions pattern |
| 502 from Application Gateway in front of a healthy app | The gateway forwards its own host name; App Service rejects it, or an access restriction blocks the gateway's subnet | Set the back-end setting to pick the host name from the back-end address, and allow the gateway in the app's access restrictions |
| Errors spread across instances at once with no change in traffic | A dependency (database, cache, external API) is slow or down, and every request waits on it | Check the dependency's metrics; add timeouts and a circuit breaker so one slow service can't exhaust the app's threads |
Two patterns deserve their own note. Thread or connection exhaustion often masquerades as "random" 503s: synchronous calls to a slow dependency pile up until no request can be served, and the SNAT Port Exhaustion and TCP Connections detectors under Availability and Performance show it. And on Linux, a 503 with "Container didn't respond to HTTP pings on port" in the log is the port mismatch above, not a platform fault.
Step 4: mitigate, then fix
Microsoft lists three mitigations; use them in this order and keep looking for the cause while they hold.
- Restart clears a one-off state. If it works, you still don't know why, so don't stop here.
- Auto-heal restarts the worker process automatically when a rule fires: too many requests in a window, slow requests, a memory limit, or a run of specific status codes. It's available for Windows apps, Linux apps and Linux custom containers, under Diagnose and solve problems > Diagnostic Tools (or in
Web.configon Windows). Proactive auto-heal and proactive CPU monitoring (Windows) do the same without rules. These are guards, not fixes: a rule that restarts the app every hour is telling you something. - Scale. Scale up (a higher tier gives more CPU and memory per instance) or out (more instances also give fault tolerance: one instance's process can die while the others serve). Autoscale on CPU or request count keeps it in step with traffic.
Then the fix that stops it recurring is usually the deployment habit: deployment slots. Deploy to a staging slot, let the swap warm every instance (customise the warm-up path and the accepted status codes if the root needs sign-in), swap into production with no dropped requests, and swap back immediately if production misbehaves. Slots need the Standard tier or above, and settings you want to stay with a slot (a staging database connection string) are marked as slot settings.
Sources
- Troubleshoot HTTP 502 and 503 errors in Azure App Service, Microsoft Learn
- Diagnostics in Azure App Service, Microsoft Learn (Diagnose and solve problems, auto-heal, the detectors)
- Set up staging environments in Azure App Service, Microsoft Learn
- Configure a custom container for Azure App Service, Microsoft Learn (
WEBSITES_PORT) - Troubleshoot HTTP 502 errors in Azure Application Gateway, Microsoft Learn (the host-header case)
- Azure Functions hosting options, Microsoft Learn (the 230-second load-balancer timeout, shared with App Service)
Did this fix it?
Comments
No comments yet. Share what worked for you.
Sign in to comment. Comments show under a display name you choose, never your email.