Guides

8 min readUpdated

502 Bad Gateway: what it means and how to fix it

Quick answer

A 502 Bad Gateway error means a server acting as a gateway or proxy, such as a CDN, a load balancer or nginx, received an invalid response, or no usable response at all, from the server behind it. The fault is almost never in the visitor's browser. Usually the upstream application has crashed, is not listening, refused the connection or closed it mid-response, so the fix starts with finding which hop produced the 502 and reading its error log.

01What does 502 Bad Gateway mean?

A 502 is generated by a server in the middle of the request, not by the application at the end of it. RFC 9110 defines it as the status a server returns when, "while acting as a gateway or proxy", it "received an invalid response from an inbound server it accessed while attempting to fulfill the request". In practice an invalid response includes no response at all: a refused connection, a connection closed before any headers arrived, or headers the proxy could not parse.

Most sites have several such middle servers. A request can pass a CDN or WAF edge, a load balancer, a reverse proxy such as nginx on the host, and finally an application server such as PHP-FPM, Gunicorn or a Node.js process. Any hop that forwards to the next can return a 502 when the next one misbehaves, so the first job is always to find out which hop wrote the error page.

02Which server returned the 502?

Answer this before changing anything. A branded page from your CDN or WAF means the edge got no valid answer from your origin; a bare "502 Bad Gateway" with an nginx footer usually comes from the proxy on your own server. Response headers such as Server and Via, which curl -sI shows, name the last hop that answered.

Where the 502 is generatedTypical causeFirst check
CDN or WAF edgeOrigin down, origin firewall rejecting the edge, TLS to the origin failing, wrong origin addressRequest the origin directly with curl --resolve; check its firewall
Load balancerTarget reset the connection, or closed an idle keepalive connection the balancer reusedCompare the target's keep-alive timeout with the balancer's idle timeout
Reverse proxy on the host (nginx)Application not listening; socket path or permissions wrong"connect() failed" in the error log; ss -ltnp
Application server (PHP-FPM, Gunicorn, Node.js)Worker crashed, or was killed by its own timeout or for memoryApplication and kernel logs at the same second
The application itselfResponse headers too large, or not valid HTTPHeader size and cookie count of the response

03Which error log lines explain a 502?

With nginx in front of the application on Debian or Ubuntu, the reason is almost always in /var/log/nginx/error.log, written in the same second as the 502 in the access log. The table explains the lines that matter; the raw lines below were reproduced on nginx 1.22.1 against deliberately broken test backends, with addresses and paths shortened.

Log line containsWhat it meansUsual fix
111: Connection refusedNothing is listening on that address and portStart the service, or correct the port in proxy_pass
2: No such file or directoryThe Unix socket does not exist: PHP-FPM is stopped or its socket path changedStart PHP-FPM, or fix the path in fastcgi_pass
13: Permission deniedThe web server user may not open the socketSet listen.owner, listen.group and listen.mode in the pool
upstream prematurely closed connectionThe application closed the connection before sending headersFind out why the worker died: timeout, crash or memory
104: Connection reset by peerThe application reset the connection, often a reused idle oneMake the proxy's idle keepalive timeout shorter than the application's
no live upstreamsEvery server in the upstream group is marked as failed or has reached its max_conns limitRead the earlier lines, which show the real failure
upstream sent too big headerResponse headers exceeded proxy_buffer_sizeRaise proxy_buffer_size, or send fewer and smaller cookies
nginx error log lines behind real 502s (reproduced on nginx 1.22.1)
connect() failed (111: Connection refused) while connecting to upstream, upstream: "http://127.0.0.1:8000/"
connect() to unix:/run/php/php8.2-fpm.sock failed (2: No such file or directory) while connecting to upstream
connect() to unix:/run/php/php8.2-fpm.sock failed (13: Permission denied) while connecting to upstream
upstream prematurely closed connection while reading response header from upstream
recv() failed (104: Connection reset by peer) while reading response header from upstream
no live upstreams while connecting to upstream, upstream: "http://app_pool/"
upstream sent too big header while reading response header from upstream

04How do I check whether the upstream is healthy?

Test each hop from the inside out. These commands only read state and are safe on a production server. Use --resolve rather than the bare IP address: it sends the real hostname in both the Host header and the TLS server name, so the origin picks the same virtual host and certificate the proxy gets.

If step 1 fails, the application is the problem. If step 1 works but step 2 fails, look at the origin firewall, certificate or virtual host. If both work and the edge still returns 502, the edge is blocked or points at the wrong origin address.

Upstream health checks (replace example.com, 203.0.113.10, ports and service names)
# 1. the application port, on the server itself
curl -sv -o /dev/null -H 'Host: example.com' http://127.0.0.1:8000/

# 2. the origin from outside, bypassing the CDN, with the real hostname and SNI
curl -sv -o /dev/null --resolve example.com:443:203.0.113.10 https://example.com/

# 3. what is listening: TCP ports and Unix sockets
ss -ltnp | grep -E ':(80|443|8000|9000)\b'
ss -lxp | grep php

# 4. is the service running, and what did it log?
systemctl status php8.2-fpm
journalctl -u php8.2-fpm --since '-15 min'

# 5. did the kernel kill a worker for lack of memory?
journalctl -k --since '-1 hour' | grep -i 'out of memory'

05Why do 502s appear behind a CDN or WAF?

Behind a CDN or a web application firewall, the edge is the gateway. It returns a 502, or a provider-specific 5xx code such as Cloudflare's 520 for an empty or invalid origin response, whenever it cannot get a valid response from your origin, and the origin can be perfectly healthy for direct visitors while still failing for the edge.

The most common reason is the origin's own firewall. Once a proxy is in front, all traffic arrives from a small set of proxy addresses, so fail2ban, per-IP connection limits or a host's abuse filter can mistake the edge for an attacker and block it. A firewall that rejects the connection produces an instant refused connection and a 502; one that silently drops packets produces a timeout instead. Cloudflare's documentation, for example, names blocked or rate-limited Cloudflare addresses as the most common cause of its 522 connection-timeout error.

The fix is not to open everything. Allow the provider's published ranges, exempt them from per-IP limits, and keep everything else closed, so attackers cannot reach the origin address directly.

  • Allow the provider's published ranges on ports 80 and 443, and keep the list current
  • Exempt those ranges from fail2ban and per-IP limits on the origin
  • Serve a valid certificate for the hostname if the edge verifies it
  • Recheck the origin address configured at the provider after any migration

06How to fix a 502, cause by cause

Restarting the upstream clears most 502s for a while, but it does not explain why the worker died. Read the logs from just before the restart first, then fix the cause.

On nginx versions before 1.29.7, upstream keepalive stays off unless you add the keepalive directive, set proxy_http_version 1.1 and clear the Connection header, as below; from 1.29.7 those are the defaults. Either way, the idle timeout on the proxy side must be shorter than the application's.

  • Crashed service: restart it, find the crash in its log, and set Restart=on-failure in the systemd unit
  • Socket path changed after a PHP upgrade: point fastcgi_pass at the socket ss -lxp shows
  • Permission denied: set listen.owner and listen.group in the pool file to the web server user
  • Workers killed by timeouts: Gunicorn kills a worker silent for longer than its timeout (30 seconds by default), PHP-FPM at request_terminate_timeout; fix the slow request instead
  • Workers killed for memory: pm.max_children times the memory per child must fit in RAM
  • 502s after idle periods: Node.js advertises a 5-second keep-alive timeout by default and Gunicorn's threaded and async workers 2 seconds (its sync worker does not keep connections open), so keep the proxy's idle keepalive timeout below that
  • Headers too big: raise proxy_buffer_size, or set fewer cookies
An upstream block that avoids two common 502 causes (checked with nginx -t)
upstream app {
    server 127.0.0.1:8000 max_fails=3 fail_timeout=10s;
    server 127.0.0.1:8001 max_fails=3 fail_timeout=10s;
    keepalive 16;
    keepalive_timeout 4s;      # shorter than the app's own idle timeout
}

server {
    server_name example.com;
    location / {
        proxy_pass http://app;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_buffer_size 16k;     # room for large Set-Cookie headers
        proxy_buffers 8 16k;
    }
}

07What can visitors do about a 502?

Very little, because the fault is on the server side. Wait a minute and reload, since many 502s come from a restart during a deploy, and try another network if you use a VPN or corporate proxy. When reporting it, include the time, the URL and any request ID on the error page, which lets the operator find the exact log line.

08Do 502 errors hurt SEO?

A short burst does not; a persistent one does. Google's documentation says 5xx responses make its crawlers slow down, indexed URLs are kept at first but eventually dropped if the errors continue, and crawling recovers once the server returns 2xx again. For planned work, a 503 with Retry-After is the correct signal; an HTTP flood that exhausts your workers is a reason to filter traffic in front of the origin.

Questions

Frequently asked questions

Almost never. A 502 is generated by a server between you and the website, such as a CDN, load balancer or reverse proxy, when the server behind it fails to give a valid response. Reloading after a minute, or trying another network if you use a VPN or corporate proxy, is all a visitor can usefully do. The site operator has to fix the upstream service.

Both come from a gateway or proxy. A 502 means the upstream answered with something invalid or nothing at all, for example a refused or reset connection, and it usually appears instantly. A 504 means the upstream did not answer within the proxy's timeout, so it appears only after a delay, often exactly 60 seconds with default nginx settings.

Intermittent 502s usually have one of three causes. The application closes idle keepalive connections sooner than the proxy expects, so a reused connection is reset. One server in a pool is unhealthy while the others are fine. Or workers are recycled or killed under load. Match the timestamps of the 502s in the access log against the error log and the application log to see which.

Yes. An application-layer flood can exhaust the application's workers or memory until they crash or are killed, and the proxy in front then reports 502s. The same symptom appears when attackers reach the origin directly and its firewall starts blocking everything, including the legitimate proxy. Absorbing the flood before it reaches the origin, and keeping the origin address private, addresses both.

On Debian and Ubuntu packages the default is /var/log/nginx/error.log. A configuration can set a different file per server block, so the reliable way to find every location is to dump the full configuration with nginx -T and search it for error_log. Look at the entries written in the same second as the 502; the cause is usually stated there in plain words.

Sources

Sources and verification

Every nginx log line in the table was reproduced, and the upstream snippet checked, with nginx 1.22.1 (nginx -t and live requests against local test backends, including a single-server group at its max_conns limit); the curl 7.88.1, ss (iproute2 6.1) and journalctl (systemd 252) commands were run on Debian 12, October 2026.

  1. 01RFC 9110, HTTP Semantics, section 15.6.3: 502 Bad Gateway · RFC Editor
  2. 02Module ngx_http_proxy_module · nginx.org
  3. 03Module ngx_http_upstream_module (max_fails, fail_timeout, keepalive) · nginx.org
  4. 04PHP-FPM configuration directives · php.net
  5. 05Gunicorn settings: timeout and keepalive · Gunicorn
  6. 06Node.js HTTP: server.keepAliveTimeout · Node.js
  7. 07Troubleshoot your Application Load Balancers: HTTP 502 · AWS
  8. 08Error 522: connection timed out · Cloudflare docs
  9. 09How HTTP status codes affect Google's crawlers · Google Search Central

Last updated