On this page
  1. What does each status code mean?
  2. How do you tell them apart quickly?
  3. What usually causes each one?
  4. How should clients and services respond?
  5. Where AI coding assistants get this wrong
  6. Checklist
  7. FAQ
    1. Is a 502 always the proxy’s fault?
    2. What is the difference between 503 and 502?
    3. Should a load balancer return 503 or 504 when all backends are down?
    4. Can a 504 be caused by the client?
  8. Related topics
  9. Sources
comparison

502 vs 503 vs 504: What's the Difference?

502 is a bad upstream response, 503 means the service cannot serve now, and 504 is a proxy timeout. Each points somewhere different.

Quick answer

  • 502 Bad Gateway: a proxy reached the upstream but got an invalid response or no response at all.
  • 503 Service Unavailable: the server is reachable but cannot handle the request now, often due to overload, maintenance, or no healthy backends.
  • 504 Gateway Timeout: the proxy reached the upstream and waited past its timeout without a complete response.
  • 500 Internal Server Error: your application ran and failed; it is not a gateway error and the app logs hold the cause.
  • Retry-After on a 503 is a signal to back off, not to retry immediately.

What does each status code mean?

502 is generated by an intermediary: a reverse proxy, load balancer, or platform router could not obtain a valid response from the server behind it. The upstream may be down, listening on the wrong port, or closing the connection early. Your application may never have seen the request.

503 is generated by the server or the load balancer and says the service cannot handle traffic right now. It is the correct code for maintenance, overload, or a pool with no healthy instances. 504 is also generated by an intermediary, but specifically because a timeout expired while waiting for the upstream response.

| Code | Generated by        | Meaning                                  | First place to look            |
|------|---------------------|------------------------------------------|--------------------------------|
| 502  | Proxy / LB          | Invalid or empty upstream response       | Proxy error log, upstream port |
| 503  | Server / LB         | Cannot serve now: overload/maintenance   | Health checks, capacity, config|
| 504  | Proxy / LB          | Upstream timed out                       | Slow query, pool, timeouts     |
| 500  | Application         | Handler raised an error                  | Application logs and traces    |

How do you tell them apart quickly?

Read which component produced the response. A 502 or 504 usually comes with the proxy’s error page or header, and the proxy’s log names the upstream it tried. A 503 often comes from the load balancer with a Retry-After header. A 500 comes from the application and usually includes a request ID you can trace.

Then test the upstream directly from its own host. If a direct request succeeds while the proxy returns 502, the problem is between them: a bind address, a port, a network policy, or a TLS mismatch. If the direct request also fails, the problem is the app or the process, and the gateway is only reporting it.

What usually causes each one?

502 clusters around process and configuration mismatches: the app crashed, is bound to localhost, listens on a different port, or the container is still starting. 503 clusters around capacity and routing: all instances failed their health check, a deploy removed the last healthy backend, a rate limit kicked in, or the service is in maintenance.

504 clusters around slowness: a query without an index, an exhausted connection pool, a downstream API that hangs, or a lock held too long. A 504 is a symptom of latency, so the fix is usually to make the slow path faster or to lower the timeout and fail earlier, not to restart the proxy.

How should clients and services respond?

Retry 502 and 504 with exponential backoff and jitter, because they often indicate a transient upstream problem. Retry 503 only after the Retry-After interval, and treat a sustained 503 as a signal to shed load rather than to hammer the service. Do not retry 500 blindly, because the request may have partially succeeded.

For non-idempotent requests, retries need an idempotency key or a check before repeating. A retried payment that already succeeded is worse than a failed request. The status code tells you whether the failure happened at the gateway or in the app, and that determines whether retrying is safe.

Where this bites vibecoders

Generated apps often return a generic 500 for every failure, so a capacity problem and a bug look identical in logs. Mapping the actual conditions to 502/503/504 and adding a Retry-After where appropriate makes incidents diagnosable and lets clients back off instead of retrying in a tight loop.

Where AI coding assistants get this wrong

  • Returning 500 for maintenance or overload instead of 503.
  • Retrying a 503 immediately with no backoff.
  • Restarting the proxy when the upstream is simply slow.
  • Assuming a 502 means application code is broken.
  • Retrying a non-idempotent request after a 504.
  • Ignoring Retry-After and hammering a service that is shedding load.

Checklist

  • Identify which component generated the error.
  • Read the proxy log for the upstream address and reason.
  • Test the upstream directly from its host.
  • Map overload and maintenance to 503 with Retry-After.
  • Retry 502/504 with backoff and jitter.
  • Never retry a non-idempotent request without an idempotency key.

FAQ

Is a 502 always the proxy’s fault?

No. The proxy emits the code, but the cause is usually the upstream being unreachable or returning something invalid. The proxy is reporting a problem it observed. Read its log to see whether the connection was refused, reset, or timed out, and then fix the upstream.

What is the difference between 503 and 502?

A 502 means the proxy could not get a usable response from the upstream. A 503 means the service is deliberately saying it cannot handle the request right now, often because of capacity, maintenance, or no healthy backends. A 503 is a statement about availability; a 502 is a failure to communicate.

Should a load balancer return 503 or 504 when all backends are down?

503 is the better fit: the service is unavailable and the client should come back later. A 504 implies a timeout, which is misleading when nothing was reachable. Whatever you choose, make it consistent and document it so clients and dashboards interpret it correctly.

Can a 504 be caused by the client?

Indirectly, if the client sends a request that triggers an expensive operation that outlasts the gateway timeout. The fix is still on the server: bound the work, stream progress, or move it to a background job and return a job identifier. A gateway timeout is not a reason to raise the timeout indefinitely.

Sources

Share: