502 Bad Gateway vs 504 Gateway Timeout (and 503)
All three come from the layer in front of your application, which is why the application's own logs are often silent. The code tells you which question to ask: did the backend answer badly, not answer in time, or was it never asked?
Modern sites rarely answer requests directly. A request might pass through a CDN, a load balancer and a reverse proxy such as nginx or Caddy before it reaches the application. When something goes wrong between those layers, the layer in front reports it with a 502, 503 or 504. Knowing which layer generated the error, and which of the three it chose, cuts debugging time dramatically.
The definitions, side by side#
| Code | RFC 9110 meaning | Plain English | First suspect |
|---|---|---|---|
502 Bad Gateway | "while acting as a gateway or proxy, received an invalid response from an inbound server" (§15.6.3) | I asked the backend and got garbage, or a slammed door | App down, crashed, or listening on the wrong port |
503 Service Unavailable | "currently unable to handle the request due to a temporary overload or scheduled maintenance" (§15.6.4) | Not now; try later | Maintenance mode, no healthy backends, load shedding |
504 Gateway Timeout | "did not receive a timely response from an upstream server" (§15.6.5) | I asked the backend and it took too long | Slow query, blocked worker, upstream dependency hanging |
One more detail from RFC 9110: a 503 "MAY send a Retry-After header field," and the spec notes that a server is not obliged to use 503 at all when overloaded: "Some servers might simply refuse the connection." That refused connection, seen from a proxy, often becomes a 502.
Who generated the error?#
Before reading any logs, find out which hop produced the response. The error page and headers usually give it away:
curl -sI https://example.com/ | grep -iE "^(HTTP|server|via|cf-ray|x-amz-cf-id|x-cache)"
- A
server: cloudflareheader with a52xcode means Cloudflare could not get a good answer from your origin. Cloudflare's own 5xx guide lists them: 521 web server down, 522 connection timed out, 524 a timeout occurred, and so on. server: nginxorserver: Caddywith a bare error page usually means your own reverse proxy could not reach the app.- A load balancer error (for example AWS's
awselb) means the balancer could not reach a healthy target.
502 Bad Gateway: common causes#
| Cause | Clue | Fix |
|---|---|---|
| Application process not running | Connection refused in the proxy log | Restart it; find out why it died (OOM killer, crash on boot) |
| Proxy pointed at the wrong host, port or socket | 502 on every request since a config change | Check proxy_pass / reverse_proxy target against what the app listens on |
| App crashed mid-request | "upstream prematurely closed connection" | App log for the crash; often memory or an unhandled signal |
| Keep-alive mismatch | Intermittent 502s under light load | Make the backend's keep-alive timeout longer than the proxy's idle timeout |
| Malformed or oversized response headers | "upstream sent too big header" | Smaller cookies/headers, or raise proxy buffer sizes |
| TLS problem between proxy and backend | Handshake errors in the proxy log | Certificate, SNI or protocol mismatch on the upstream |
| Deploy restarting all instances at once | A burst of 502s every deploy | Rolling restarts, health checks, connection draining |
The keep-alive row deserves emphasis because it is so confusing in practice. AWS's Application Load Balancer documentation lists, among 502 causes, a target that "closed the connection with a TCP RST or a TCP FIN while the load balancer had an outstanding request," and advises: "Check whether the keep-alive duration of the target is shorter than the idle timeout value of the load balancer" (AWS docs). The same race exists between nginx and many application servers.
504 Gateway Timeout: common causes#
| Cause | Clue | Fix |
|---|---|---|
| Slow database query | Only certain pages or filters time out | Query plan, indexes, pagination |
| Worker pool exhausted | Everything slows down together under load | More workers, async I/O, shed load earlier |
| Blocking call to a slow third-party API | Timeouts track the dependency's status page | Client-side timeouts shorter than the proxy's; circuit breakers |
| Long-running job done in the request | Report or export endpoints fail at a round number of seconds | Move to a background job and return 202 Accepted |
| Network or firewall drops | Timeouts with no load on the app | Security groups, ACLs, routing between proxy and app |
That "round number of seconds" is a useful tell. The defaults are documented: nginx's proxy_connect_timeout, proxy_read_timeout and proxy_send_timeout are each 60 seconds by default; Cloudflare's Proxy Read Timeout behind its 524 error is 125 seconds by default; and AWS's ALB lists a 10-second connection timeout among its 504 causes. Caddy is different: its response_header_timeout has no timeout by default, while dial_timeout defaults to 3 seconds.
Reading the proxy's log#
When the app log shows nothing, the proxy's error log is where the story is. Typical nginx error.log lines and what they mean:
connect() failed (111: Connection refused) while connecting to upstream → 502, nothing listening
upstream prematurely closed connection while reading response header → 502, app died or closed early
upstream timed out (110: Connection timed out) while reading response header → 504, app too slow
no live upstreams while connecting to upstream → 502, every backend marked failed
# nginx
sudo tail -n 100 /var/log/nginx/error.log | grep -i upstream
# Caddy under systemd
journalctl -u caddy --since "15 min ago" | grep -iE "dial|timeout|reverse_proxy"
# Can the proxy host reach the app at all?
curl -sv http://127.0.0.1:8000/health
The last command is the quickest test there is. If the proxy host itself cannot get a fast 200 from the app, the problem is not the proxy.
503: use it on purpose#
503 is the one code in this group you should send deliberately. Google's planned-downtime guidance says that instead of a 404 or an error page with 200, "it's better to return a 503 HTTP result code (Service Unavailable) which tells search engine crawlers that the downtime is temporary," with an optional Retry-After (Google, 2011). It also warns that "lasting 503 HTTP result codes can eventually be seen as a sign that the server is now permanently unavailable."
Google's current guide on pausing a site adds two practical rules: a 503 is for urgently disabling a site for "1-2 days," and you should not "return a 503 HTTP response status code for the robots.txt file because this blocks all crawling."
HTTP/1.1 503 Service Unavailable
Retry-After: 1800
Content-Type: text/html; charset=utf-8
Cache-Control: no-store
Maintenance page served as 200 OK (or a redirect to /maintenance)
Maintenance page served as 503 with Retry-After, on the original URLs
A 200 tells crawlers and monitors the maintenance page is your content. A redirect moves them away from the real URL. A 503 says "same page, come back later."
Raising every timeout to 10 minutes to make 504s go away
Fix the slow path, set client timeouts shorter than proxy timeouts, move long work to background jobs
Long timeouts keep workers busy waiting, so one slow dependency can exhaust capacity for everything else. Google's SRE book chapter on cascading failures describes exactly this pattern and recommends load shedding and deadlines instead.
If you are a visitor#
- Wait a minute and reload. Many 502s happen during a deploy or restart and clear quickly.
- Check the site's status page; these errors often mean a wider outage.
- If only one site fails and every other site works, it is not your connection.
- For a 504 after submitting a form, check whether the action completed (for example, an order confirmation email) before submitting again.
Frequently asked questions
What is the difference between 502 and 504?
Both are sent by a gateway or proxy. 502 Bad Gateway means it received an invalid response from the upstream server (including a refused or reset connection). 504 Gateway Timeout means it did not receive a timely response from upstream.
What is the difference between 503 and 502?
503 Service Unavailable means the server is temporarily unable to handle the request because of overload or maintenance, and it may include Retry-After. 502 specifically means a proxy got a bad response from the server behind it.
Is a 502 or 504 error my fault?
Almost never. These are server-side problems between the site's proxy or CDN and its application servers. Waiting a minute and reloading is the main thing a visitor can do.
How do I fix a 504 Gateway Timeout?
Find out why the upstream is slow (slow queries, blocking calls, an overloaded worker pool) and fix that first. Raising proxy timeouts is sometimes appropriate for genuinely long requests, but long work is usually better moved to a background job that returns 202 Accepted.
Which code should I use for planned maintenance?
503 with a Retry-After header, and keep robots.txt available. Google recommends 503 for short planned downtime, warns that long-lasting 503s can lead to URLs being removed, and says not to return 503 for robots.txt because that blocks all crawling.
Sources
Every factual claim on this page links to one of these. Checked October 2026. See how we source.
- RFC 9110 §15.6.3: 502 Bad Gateway rfc-editor.org
- RFC 9110 §15.6.4: 503 Service Unavailable rfc-editor.org
- RFC 9110 §15.6.5: 504 Gateway Timeout rfc-editor.org
- RFC 9110 §10.2.3: Retry-After rfc-editor.org
- nginx: ngx_http_proxy_module (timeouts) nginx.org
- Caddy: reverse_proxy directive caddyserver.com
- AWS: Troubleshoot your Application Load Balancers docs.aws.amazon.com
- Cloudflare: Error 524 developers.cloudflare.com
- Cloudflare: Cloudflare 5xx errors developers.cloudflare.com
- Google Search Central: Pause or disable a website developers.google.com
- Google Search Central: How to deal with planned site downtime (2011) developers.google.com
- Google SRE Book: Addressing Cascading Failures sre.google