How to Fix Nginx 503 Service Unavailable
Nginx returns 503 when every upstream is marked unhealthy or the backend can't respond in time. A mismatch between the health check path in nginx.conf and the path the app actually exposes is the most common silent trigger - nginx declares the backend down before a real user even connects.
What a 503 Service Unavailable actually means
Unlike a 502 (no connection), nginx returns 503 when the upstream exists but
is considered unavailable - either the health check keeps failing, or all
upstream connections are occupied / timed out. The backend can be running fine
and the error still fires.
Step 1: read the error log
tail -50 /var/log/nginx/error.log
Two messages that commonly appear together:
upstream timed out (110: Connection timed out) while reading response header
from upstream, upstream: "http://127.0.0.1:8080/"
no live upstreams while connecting to upstream
The first line tells you how each request failed; the second tells you nginx has marked every backend server down.
Step 2: hit the backend directly
curl -v http://127.0.0.1:8080/
time curl http://127.0.0.1:8080/ # check if it's just slow
If the backend answers correctly but takes a few seconds, the issue is
proxy_read_timeout - not the backend itself.
Step 3: check the health check path
Open /etc/nginx/nginx.conf and find the health check location or upstream
health_check directive:
upstream backend {
server 127.0.0.1:8080;
}
server {
location /health { # <- the path nginx polls
proxy_pass http://backend;
}
}
Now check what path the app actually exposes:
curl http://127.0.0.1:8080/healthz # returns 200
curl http://127.0.0.1:8080/health # returns 404
A path mismatch (/health in nginx vs /healthz on the app) means every
health probe returns 404, nginx marks the upstream unhealthy, and all
requests get 503 - even while the app handles real traffic fine.
Step 4: fix both problems and reload
Correct the health check path and raise the read timeout to something realistic (30-60 seconds is common; 1 second is almost never right):
upstream backend {
server 127.0.0.1:8080;
}
server {
proxy_read_timeout 30s; # was 1s - too tight for any real request
location / {
proxy_pass http://backend;
}
location /healthz { # matches the app's actual endpoint
proxy_pass http://backend;
}
}
Then validate and reload - no full restart needed:
nginx -t # check config syntax
nginx -s reload # graceful reload
curl http://localhost/ # should return 200
Why these two bugs compound
A too-short proxy_read_timeout causes individual requests to fail, which
makes nginx increment the upstream's failure count. If failures cross the
threshold, nginx stops sending new requests to that upstream entirely - turning
intermittent timeouts into a complete outage. Fix the health path first (so
nginx trusts the upstream again), then fix the timeout (so slow requests don't
re-trigger it).
Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.
Try it in a workspace →What you'll practice
- Reading nginx error logs to distinguish timeout failures from connection failures
- Matching the health check path in nginx.conf to the path the backend exposes
- Setting a realistic proxy_read_timeout and reloading nginx safely
FAQ
What causes nginx 503 Service Unavailable?
Nginx returns 503 when it marks every upstream server as unhealthy (usually because health checks are failing) or when all upstream connections are timing out. A health check path mismatch - where nginx polls /health but the app exposes /healthz - is a common silent trigger.
How do I find what's causing the nginx 503?
Run tail -50 /var/log/nginx/error.log to see upstream timeout or 'no live upstreams' messages, then curl the backend directly to confirm it's actually running. Compare the health check path in nginx.conf to the paths the app exposes.
What should proxy_read_timeout be set to in nginx?
Typically 30-60 seconds for a web application. The default is 60s. A value of 1s is almost always too tight and will cause timeouts on anything but the fastest responses, which nginx counts as upstream failures.
How do you fix a 503 Service Unavailable in nginx?
A 503 means nginx cannot get a healthy response from the upstream. Confirm the backend is running and listening, check the health-check path matches, and raise proxy_read_timeout if the upstream is just slow. Then reload nginx.
Keep learning
Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.
Open the workspace →