/ โ† HTTP Error Fixes / 502 Errors
In this guide
  1. What sits behind a 502
  2. The error log line that names the cause
  3. The fix, step by step
  4. When 502s appear only under load
  5. 502s during deploys and restarts
  6. Intermittent 502s from reused connections
  7. When the CDN reports the 502
  8. Keeping 502s away
  9. Questions and answers

What sits behind a 502

Almost every modern site has at least two programs involved in answering a request. A front server receives the connection and handles TLS, static files and caching. It then hands dynamic requests to a back-end program, called the upstream, which runs your PHP, Node, Python or Ruby code. A 502 means the front server tried to talk to the upstream and the conversation failed: it could not connect, the upstream hung up part way through, or it sent back something that was not a valid HTTP response.

The important point is that the program showing the error page is not the program at fault. nginx did its job by reporting the failure. Restarting nginx rarely helps; the fix is almost always in the upstream or in how the two are wired together.

Common chains and where a 502 can start

Set-upFront serverUpstream that usually failed
WordPress or other PHP on a VPSnginxPHP-FPM pool
Plesk or cPanel hostingnginx in front of ApacheApache, or PHP-FPM behind it
Node, Next.js or Express appnginx or a platform routerThe Node process
Site behind Cloudflare or another CDNThe CDN edgeYour origin server as a whole
Cloud load balancer in front of several serversThe load balancerOne or more unhealthy instances

With a CDN in front there can be two gateways in a row, and either one can produce the 502. Step 1 below sorts that out before you start digging.

๐Ÿ’ก A 502 and a 504 look alike but point in different directions. A 502 means the upstream failed or answered badly; a 504 means the proxy waited and gave up. If your error is a 504, the 504 Gateway Timeout guide is the one you need.

The error log line that names the cause

nginx writes one line to its error log for every 502, and the wording tells you the cause. On Debian and Ubuntu the file is usually /var/log/nginx/error.log; hosting panels often keep a separate error log per domain. Search it for the minute when the audit reported the failure.

sudo grep -i "upstream" /var/log/nginx/error.log | tail -20
Message fragmentWhat it meansWhere to look next
connect() to unix:/run/php/... failed (2: No such file or directory)The PHP-FPM socket file does not exist, so the pool is stopped or uses another pathIs PHP-FPM running? Which socket does the pool create?
(111: Connection refused) while connecting to upstreamNothing is listening on that port or socketThe app has crashed, or is on a different port
(13: Permission denied) while connecting to upstreamnginx is not allowed to open the socketSocket owner and mode in the pool config; SELinux on some distributions
(11: Resource temporarily unavailable)The upstream's queue of waiting connections is fullToo few workers for the traffic; see the capacity section
upstream prematurely closed connection while reading response headerThe upstream accepted the request then died or hung up before answeringApplication and PHP-FPM logs for a crash on that request
upstream sent too big header while reading response headerThe response headers were larger than nginx's bufferLarge cookies or many Set-Cookie headers; step 6
recv() failed (104: Connection reset by peer)The upstream reset the connectionA worker being killed, or an idle keep-alive connection closed

Apache used as a reverse proxy logs similar failures through mod_proxy, with messages such as "AH01102: error reading status line from remote server" or "AH00957: attempt to connect to ... failed".

The fix, step by step

Step 1

Find out which layer returned the 502

Look at the error page and the response headers. A plain page saying nginx came from your own server. A Cloudflare-branded page came from the CDN edge, and a response with a cf-ray header but an origin-styled page came from your server through Cloudflare. Then request the origin directly, bypassing the CDN, with curl -I --resolve www.example.com:443:203.0.113.10 https://www.example.com/ using your server's real IP. If the origin answers 200, the fault is between the CDN and your server, not in your application.

Step 2

Read the proxy error log at the failure time

Take the time of a failure from the HTTP Error Audit results and open the front server's error log for that minute. Match the message against the table above: a missing socket, a refused connection, a permission error, a full queue, an upstream that closed early, or headers that were too large. Each points to a different fix, so write down the exact wording. If the log is empty for that time, you are probably reading the wrong file, or the 502 came from a CDN or load balancer rather than your own server.

Step 3

Check the upstream process is running and listening

Run systemctl status php8.3-fpm, adjusting the version to yours, or pm2 list for a Node app, and look for a stopped or repeatedly restarting service. Then confirm something is actually listening with ss -ltnpx, which lists TCP ports and Unix sockets with the owning process. If the service is down, start it and reload the page. If that clears the 502, you have restored service, but you still need step 5 to find out why it stopped.

Step 4

Match the socket or port on both sides

Compare the address in the proxy config with the address the upstream opens. For PHP-FPM, the fastcgi_pass path in nginx must match the listen line in the pool file, which changes whenever PHP is upgraded to a new version. For Node, the proxy_pass port must match the port the app binds to. Use 127.0.0.1 rather than localhost on both sides, because localhost can resolve to the IPv6 address ::1 while the app listens only on IPv4.

Step 5

Find out why the process died

Read the upstream's own log, not the proxy's. Check the PHP-FPM log for children exiting on a signal or the pool hitting pm.max_children, and run journalctl -u php8.3-fpm --since "1 hour ago" or pm2 logs --lines 200 for Node. Run dmesg -T | grep -i "killed process" to see whether the kernel's out-of-memory killer ended it. A memory kill needs less memory per worker, fewer workers or more RAM; a crash on one URL needs a code fix.

Step 6

Raise buffers for oversized response headers

If the log says the upstream sent too big a header, nginx's buffer for the first part of the response is smaller than the headers your application sends. This happens with large or numerous cookies, long Link headers and some debugging plugins. For PHP set fastcgi_buffer_size 32k; and fastcgi_buffers 16 16k;, and for proxied apps set proxy_buffer_size and proxy_buffers to similar values. Test the config with nginx -t and reload. Also ask why the headers are so large.

Step 7

Set the application to restart itself

Make a crash cost seconds rather than hours. For a systemd service, add Restart=on-failure and RestartSec=2 in an override with systemctl edit. For Node under PM2, keep automatic restart on and add max_memory_restart so a leaking process is recycled before the kernel kills it. For PHP-FPM, set pm.max_requests so each child is replaced after a number of requests. Then run the HTTP Error Audit again at a busy time to confirm the 502s have stopped.

When 502s appear only under load

A site that works for you but returns 502s during a crawl, a sale or a traffic spike is usually running out of workers or memory. Each PHP-FPM child or Node process handles a limited number of requests at once. When all are busy, new connections queue; when the queue is full, the proxy cannot connect and reports a 502.

For PHP-FPM, the PHP-FPM log will contain a warning that the pool reached its pm.max_children setting. Raising the limit helps only if the server has memory to spare. A rough way to size it:

  1. Find the typical memory per PHP-FPM child with ps -ylC php-fpm8.3 --sort:rss (the process name varies by distribution) and read the RSS column, which is in kilobytes.
  2. Decide how much RAM PHP may use after leaving room for the database, the web server and the operating system.
  3. Divide that figure by the memory per child. The result is a sensible ceiling for pm.max_children.

Setting the limit higher than memory allows turns queueing into out-of-memory kills, which produce more 502s, not fewer. If the numbers do not work, the answer is caching whole pages so fewer requests reach PHP at all, or more RAM.

; /etc/php/8.3/fpm/pool.d/www.conf (path varies)
pm = dynamic
pm.max_children = 12
pm.start_servers = 4
pm.min_spare_servers = 2
pm.max_spare_servers = 6
pm.max_requests = 500

For Node, a single process uses one CPU core for JavaScript. Running one process per core with PM2 cluster mode or several containers behind the proxy spreads the load, and lets one process restart while others carry on serving.

502s during deploys and restarts

If 502s cluster around the time you release code, the application is being stopped before the new one is ready. For a few seconds nothing is listening, and every request in that gap fails.

Intermittent 502s from reused connections

Some 502s come and go with no crash in any log. A common cause is connection reuse between a load balancer or proxy and the app. To save time, the proxy keeps idle connections open and reuses them. If the application closes an idle connection at the very moment the proxy sends a new request down it, the request fails and the proxy reports a 502, often logged as a connection reset.

The rule is that the upstream should keep idle connections open for longer than the proxy in front of it does. Node's HTTP server has a default keepAliveTimeout of a few seconds, which is shorter than the idle timeout of many cloud load balancers. Raising it above the load balancer's value, and setting headersTimeout a little higher still, removes this class of 502:

const server = app.listen(3000, '127.0.0.1');
// Longer than the load balancer's idle timeout (check your provider's setting)
server.keepAliveTimeout = 65000;
server.headersTimeout = 66000;

More on Node process management, PM2 and platform limits is in the Node.js and Next.js HTTP errors guide.

When the CDN reports the 502

If requests straight to the origin succeed but visitors get a 502 through the CDN, the edge cannot reach or understand your server. Check that the CDN's IP ranges are allowed through the server firewall and any rate limiting, that the origin address in the CDN settings is current after a server move, and that the origin can complete a TLS connection using the mode the CDN expects. Cloudflare uses its own codes in the 520 to 526 range for many origin problems, and its documentation lists what each means; those are worth reading alongside your origin logs. Certificate problems between the CDN and origin are covered in the SSL errors guide.

โš ๏ธ Do not switch off the CDN or open the firewall to everything as a test on a live site. Use a --resolve request from a trusted machine to reach the origin directly instead, so you can compare the two paths without exposing the server.

Keeping 502s away

Questions and answers

1. Is a 502 Bad Gateway my fault or my host's?

It depends on which layer failed. If the error page is branded by your CDN and requests straight to your server work, the problem is between the two, often a firewall or an origin setting. If your own server returns the 502, the application behind the web server has failed or is misconfigured, which is usually within your control on a VPS. On shared or managed hosting you may not be able to see or restart PHP-FPM yourself, so send your host the URL and exact times.

2. Why does refreshing the page sometimes fix a 502?

Because the cause was brief. A PHP-FPM child may have crashed on one request and been replaced, a Node process may have restarted after an error, or a reused connection may have been closed at the wrong moment. The next request found a healthy process. That makes the 502 easy to dismiss, but a pattern of short failures usually means a real crash, memory pressure or a connection timing problem that will get worse as traffic grows, so check the logs anyway.

3. Does restarting nginx fix a 502 error?

Rarely. nginx is reporting that the program behind it failed; it is not usually the cause. Restarting PHP-FPM or the Node app may bring the site back if that process had stopped, but it will fail again unless you find out why it stopped. Read the nginx error log line for the failed request first, then the upstream's own log. The message usually tells you whether you are looking at a crash, a wrong socket path, a permission problem or too little memory.

4. Do 502 errors affect my Google rankings?

Short, occasional 502s are unlikely to have a lasting effect. Search engines treat 5xx responses as temporary server problems and try again later. If they keep meeting server errors, they slow down how often they crawl the site, and pages that fail for a long period can eventually drop out of the index until they recover. Fixing the cause quickly and confirming it with a fresh crawl is what matters, rather than any one short outage.

5. Why do I get a 502 only on one page or one form?

A 502 limited to one URL usually means the application crashes while building that particular page, for example because of a fatal error in a plugin used only there, a request that uses far more memory than others, or response headers that are unusually large because of many cookies. Look at the upstream log at the moment you load that page. If nginx reports that the upstream sent too big a header, increase the buffer sizes; otherwise the fix is in the code.

6. What is the difference between 502, 503 and 504?

A 502 means a gateway or proxy got an invalid answer, or no answer, from the server behind it. A 503 means a server is temporarily unable to handle the request, often because of maintenance mode, overload or rate limiting, and it may send a Retry-After header. A 504 means the gateway waited for the server behind it and gave up because it took too long. They point to crashes, deliberate unavailability and slowness respectively, which is why the fixes differ.

See where your 502s happen

The HTTP Error Audit requests every URL in your crawl scope, records the status code and response time, follows redirects and flags anything outside the 200 range, giving you the URLs and times to match against your server logs.

Open HTTP Error Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All HTTP Error Fixes  ·  504 Gateway Timeout Errors  ·  HTTP Errors in Node.js  ·  HTTP Error Audit Guide  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.