502 Bad Gateway: How to Fix It in Nginx, Cloudflare and Docker

A practical, layer-by-layer guide to finding and fixing 502 Bad Gateway errors across Nginx, Cloudflare, Docker, application services and PHP-FPM.

Compare VPS PlansRead Nginx Guide
Diagram showing a 502 Bad Gateway failure between Nginx and Docker behind Cloudflare

# 502 Bad Gateway: How to Fix It in Nginx, Cloudflare and Docker

A 502 Bad Gateway error means that one server accepted your request but received an invalid response from the next server in the chain.

That first server might be Cloudflare, Nginx, Caddy, Apache, a load balancer or another reverse proxy. The upstream might be a Node.js application, PHP-FPM, a Python service, a Docker container or another web server.

The browser only sees the final 502 page. The useful evidence is usually one layer deeper.

This guide provides a repeatable way to diagnose and fix 502 errors on a Linux server. It covers the most common combinations:

  • Nginx in front of a local application.
  • Nginx in front of Docker containers.
  • Cloudflare in front of Nginx or another origin server.
  • Nginx connected to PHP-FPM.
  • Applications managed by systemd.
  • Intermittent errors caused by memory, disk, load or timeouts.

The goal is not to restart everything until the error disappears. It is to identify the failed connection, correct it and make the service less likely to fail again.

What Does 502 Bad Gateway Mean?

In a typical deployment, a request follows a chain like this:

Visitor -> Cloudflare -> Nginx -> Application -> Database

Each component depends on the next one. Nginx may be healthy and serving its own error page, but the application behind it may be stopped, listening on the wrong port or returning an invalid response.

HTTP status codes help narrow down where the failure occurred:

ErrorGeneral meaning
500 Internal Server ErrorThe application or server encountered an internal error.
502 Bad GatewayA gateway received an invalid response from an upstream.
503 Service UnavailableThe service is temporarily unavailable or deliberately in maintenance.
504 Gateway TimeoutThe upstream did not respond before the gateway's timeout.
521 to 526Cloudflare-specific origin connectivity or TLS failures.

A 502 normally requires action by the website or server operator. Clearing a browser cache rarely fixes the underlying problem.

The Fastest 502 Diagnostic Workflow

Work through the request path in order:

  1. Reproduce the error and record the exact time.
  2. Identify which proxy generated it.
  3. Test the origin without the CDN where possible.
  4. Check whether the reverse proxy is healthy.
  5. Test the upstream application directly.
  6. Read the proxy and application logs.
  7. Check ports, sockets, DNS and container networks.
  8. Check CPU, memory, disk and recent deployments.

This approach is faster than changing several settings at once because each test removes an entire category of possible causes.

Step 1: Confirm the Error and Capture the Response

Test the affected URL from a terminal:

curl -vI https://example.com/

For an API endpoint, use the correct method rather than assuming a HEAD request behaves like a normal request:

curl -v https://example.com/api/health

Record:

  • The exact URL.
  • The UTC time of the failure.
  • Whether every request fails or only one path fails.
  • Response headers such as server, cf-ray and cf-error-type.
  • Whether the error affects every visitor or only one network.

If only one API path fails while the rest of the site works, the problem is probably in that application's route or upstream. If every hostname on the server fails, investigate the proxy, host resources and networking first.

Step 2: Work Out Whether Cloudflare or the Origin Returned the 502

When Cloudflare is enabled, there are two broad possibilities:

  • Your origin returned a 502 and Cloudflare passed it to the visitor.
  • Cloudflare could not obtain a valid response from the origin.

Compare the public request with a request sent directly to the origin. The following command connects to the origin IP while retaining the correct hostname and TLS Server Name Indication:

curl -v --resolve example.com:443:203.0.113.10 https://example.com/

Replace example.com and 203.0.113.10 with your hostname and origin address.

Interpret the result:

  • Public request fails and direct-origin request works: inspect Cloudflare DNS, TLS mode, origin firewall rules and Cloudflare configuration.
  • Both requests fail: the fault is almost certainly at the origin or its upstream application.
  • Direct request cannot connect: check whether the origin address is correct and whether ports 80 or 443 are listening and allowed through the firewall.
  • Only one Cloudflare location appears affected: retain the Ray ID, timestamp and affected URL for investigation.

Do not leave an origin unnecessarily exposed simply to troubleshoot it. Use a temporary, controlled test and keep firewall rules restricted.

Step 3: Check Nginx Before Changing Its Configuration

Confirm that Nginx is running:

sudo systemctl status nginx --no-pager

Validate its complete configuration:

sudo nginx -t

Check recent service logs:

sudo journalctl -u nginx -n 100 --no-pager

Then inspect the Nginx error log:

sudo tail -n 100 /var/log/nginx/error.log

Common messages are highly specific:

Connection refused

connect() failed (111: Connection refused) while connecting to upstream

Nginx reached the host but nothing accepted the connection on the configured port. The application may be stopped, restarting or listening elsewhere.

No such file or directory

connect() to unix:/run/php/php-fpm.sock failed (2: No such file or directory)

The configured Unix socket does not exist. This often happens after a PHP version changes but the Nginx configuration still points to the old PHP-FPM socket.

Permission denied

connect() to unix:/run/app/app.sock failed (13: Permission denied)

The socket exists, but the Nginx worker user cannot access it or its parent directory.

Upstream timed out

upstream timed out while reading response header from upstream

The application accepted the connection but did not respond in time. Investigate application logs, database latency, external API calls and server load before increasing timeouts.

After making a correction, validate before reloading:

sudo nginx -t && sudo systemctl reload nginx

A reload preserves active connections and is normally preferable to an unnecessary restart.

Step 4: Test the Upstream Application Directly

Find the proxy_pass, fastcgi_pass or upstream definition used by the failing server block:

sudo nginx -T | less

A common proxy configuration looks like this:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Test that upstream from the server itself:

curl -v http://127.0.0.1:3000/

Check whether anything is listening:

sudo ss -ltnp | grep ':3000'

If the application is meant to listen on port 3000 but ss shows port 3001, correct either the application configuration or proxy_pass. If it listens only on an IPv6 address, a proxy connection to 127.0.0.1 will not reach it. The address family and port must match.

For a systemd-managed application:

sudo systemctl status myapp --no-pager
sudo journalctl -u myapp -n 150 --no-pager

Replace myapp with the actual service name.

How to Fix a Docker 502 Bad Gateway Error

Docker adds another networking layer, but the diagnosis remains the same: confirm that the container is running, healthy, listening and reachable from the reverse proxy.

List all containers, including stopped ones:

docker ps -a

Read the application logs:

docker logs --tail 200 my-app

For Docker Compose:

docker compose ps
docker compose logs --tail 200 app

Check health and restart information:

docker inspect my-app --format '{{json .State}}'

Common Docker causes

#### The container exits after starting

The application may be missing an environment variable, unable to connect to its database or failing a migration. The container logs should contain the real error.

#### Nginx uses the published host port incorrectly

If Nginx runs on the host, it can usually proxy to a published address such as 127.0.0.1:3000. If Nginx runs inside the same Compose project, it should normally use the Compose service name and the container's internal port:

proxy_pass http://app:3000;

Using localhost inside the Nginx container refers to the Nginx container itself, not the application container.

#### The containers are on different networks

Inspect their networks:

docker inspect nginx --format '{{json .NetworkSettings.Networks}}'
docker inspect my-app --format '{{json .NetworkSettings.Networks}}'

Both containers need a shared Docker network if one must reach the other by service name.

#### The application listens only on localhost inside its container

An application bound to 127.0.0.1 inside one container cannot normally accept connections from another container. Configure it to listen on 0.0.0.0 inside the container, while controlling public exposure through Docker networks and published ports.

#### A health check is wrong

A container can be running but marked unhealthy because its health check uses the wrong path, hostname or port. Test the same command from inside the container and correct the health check rather than disabling it without investigation.

How to Fix a PHP-FPM 502 Bad Gateway Error

For PHP websites, Nginx normally sends .php requests to PHP-FPM over a Unix socket or TCP port.

Check installed and running PHP-FPM services:

systemctl list-units --type=service 'php*-fpm.service'

Check available sockets:

ls -la /run/php/

Your Nginx configuration may contain:

fastcgi_pass unix:/run/php/php8.4-fpm.sock;

The socket path must match the active PHP-FPM version. Then verify the service:

sudo systemctl status php8.4-fpm --no-pager
sudo journalctl -u php8.4-fpm -n 100 --no-pager

Other PHP-FPM causes include:

  • Exhausted worker pools.
  • A script that consumes too much memory.
  • Incorrect socket ownership or permissions.
  • A PHP extension crashing a worker.
  • A deployment that changed the PHP version without updating Nginx.

Do not make the socket world-writable. Set appropriate user, group and socket permissions instead.

Check Whether the Server Is Out of Resources

An application can appear healthy during a quiet test and still fail under load.

Check memory:

free -h

Check disk space and inodes:

df -h
df -i

Check load and active processes:

uptime
top

Look for out-of-memory kills:

sudo journalctl -k --since '2 hours ago' | grep -i -E 'out of memory|oom|killed process'

For containers, view live resource use:

docker stats

A full disk can prevent applications from writing logs, temporary files, sessions or database data. Memory exhaustion can cause the kernel or container runtime to kill the upstream process, leaving Nginx with nothing to contact.

If failures happen at the same time each day, also check scheduled backups, imports, report generation, log rotation and cron jobs.

Check DNS, TLS and Firewall Rules

Not every 502 is an application crash.

DNS

Confirm the public hostname resolves to the intended address:

dig +short example.com A
dig +short example.com AAAA

If an old AAAA record points to a server that is no longer configured, IPv6-capable visitors may reach a different origin from IPv4 visitors.

TLS

If Cloudflare connects to the origin over HTTPS, the origin must serve a suitable certificate and TLS configuration. Test it directly while preserving the hostname:

openssl s_client -connect 203.0.113.10:443 -servername example.com

Firewall

Check the host firewall:

sudo ufw status verbose
sudo nft list ruleset

Make sure you understand which firewall system is active before changing rules. On a proxied site, the origin must accept the required traffic from the proxy network. An application bound to localhost does not need a public firewall opening merely because Nginx proxies to it locally.

Should You Increase Nginx Timeouts?

Increasing a timeout can be appropriate for a deliberately long-running request, but it is not a general fix for 502 errors.

For example:

location /api/report {
    proxy_pass http://127.0.0.1:3000;
    proxy_connect_timeout 10s;
    proxy_send_timeout 60s;
    proxy_read_timeout 120s;
}

Before doing this, determine why the response takes so long. Common root causes include:

  • A missing database index.
  • A blocked worker queue.
  • An unreachable external API.
  • Too few application workers.
  • CPU or memory pressure.
  • Synchronous work that should be handled by a background job.

Longer timeouts can hide a performance defect while tying up more connections. Fix the bottleneck first, then choose a timeout that matches the endpoint's intended behaviour.

Quick 502 Troubleshooting Table

SymptomLikely causeFirst check
Nginx reports connection refusedApp stopped or wrong portss -ltnp and app service status
Nginx reports missing socketWrong PHP/app socket path/run/php/ or configured socket directory
Nginx reports permission deniedSocket or directory permissionsSocket owner, group and mode
Docker container is ExitedApp startup failuredocker logs
Container is running but Nginx cannot reach itWrong network, hostname or bind addressdocker inspect and direct curl
Public site fails but direct origin worksCDN, TLS, DNS or firewall issueCloudflare headers and origin configuration
Errors appear only under loadCPU, memory, workers or database pressureResource graphs, OOM logs and app metrics
Only IPv6 visitors failIncorrect AAAA record or IPv6 listenerdig AAAA and ss -ltnp
Error began after deploymentPort, environment or migration changedDeployment diff and application logs

How to Prevent 502 Errors

Once the service is restored, add protections that make the next failure shorter and easier to diagnose.

Add a meaningful health endpoint

A health check should confirm that the application can perform the minimum work required to serve traffic. A shallow check can confirm the process is alive; a separate readiness check can verify required dependencies.

Use controlled restart policies

Systemd and Docker can restart a process after a genuine crash. Avoid an unlimited rapid restart loop that hides the original error and consumes resources. Keep logs and use sensible backoff.

Monitor the complete request path

Monitor the public HTTPS URL, not only whether the server responds to ping. Track CPU, RAM, disk, application errors and response latency alongside uptime.

Validate before deploying

Useful deployment checks include:

  • Validate Nginx with nginx -t.
  • Confirm required environment variables exist.
  • Run database migrations deliberately.
  • Start the new application and pass a health check before switching traffic.
  • Keep a tested rollback path.

Retain useful logs

Keep proxy and application logs long enough to investigate intermittent incidents, but configure rotation so logs cannot fill the server.

Separate public and internal services

Expose the reverse proxy publicly and keep applications, databases and queues on localhost, private networks or internal container networks wherever possible.

Running a Reliable Web Application on HYEHOST

The troubleshooting process in this guide works on any Linux server. If you need infrastructure for the application itself, HYEHOST provides several practical routes:

  • Cloud VPS for websites, APIs, Docker stacks and reverse proxies.
  • VPS Resource Pools when an application should be split across web, database and worker VMs.
  • VDS hosting for workloads that need dedicated compute resources.
  • Dedicated servers for larger databases, busy platforms and sustained workloads.
  • Managed VPS when you would rather have help operating the server.

The HYEHOST panel provides console access, power controls, reinstall workflows, traffic visibility and service management. Console access is particularly useful when a firewall mistake or broken network configuration prevents SSH access.

For related setup guidance, read:

Further Reading

Frequently Asked Questions

Is a 502 Bad Gateway error caused by my browser?

Usually not. A 502 normally means a proxy or gateway received an invalid response from an upstream server. If the site fails for everyone, the website operator needs to investigate it. If it fails only for you, test without a VPN or local proxy and try another network before reporting it.

How do I fix 502 Bad Gateway in Nginx?

Read /var/log/nginx/error.log, identify the configured upstream, confirm the application is running and test it directly with curl. The most common causes are a stopped service, wrong port, missing Unix socket, permission error or timeout.

Why does Docker cause a 502 error?

Docker itself is not necessarily the fault. A 502 appears when the reverse proxy cannot get a valid response from the application container. Check whether the container exited, is unhealthy, listens on the expected port and shares a network with the proxy.

Can Cloudflare cause a 502 Bad Gateway?

Yes, but many Cloudflare-branded 502 pages originate from the web server behind Cloudflare. Compare a normal request with a controlled direct-origin request and inspect Cloudflare response headers to locate the failing layer.

What is the difference between 502 and 504?

A 502 means the gateway received an invalid upstream response. A 504 means the gateway waited but did not receive the upstream response before its timeout. Both require checking the proxy-to-upstream path.

Will restarting Nginx fix a 502?

It may temporarily restore service in some cases, but it will not fix a stopped application, incorrect port, broken container network or exhausted server. Read the logs and test the upstream so you correct the actual cause.

Final Checklist

When a 502 appears, check these in order:

  • Capture the URL, timestamp, status and response headers.
  • Compare the public request with a direct-origin request.
  • Confirm Nginx is running and its configuration validates.
  • Read the Nginx error log.
  • Test the configured upstream directly.
  • Check the application service or container logs.
  • Confirm ports, sockets, bind addresses and Docker networks.
  • Check memory, disk, load and OOM events.
  • Verify DNS, IPv4, IPv6, TLS and firewall rules.
  • Add health checks, monitoring and safer deployment checks after recovery.

A 502 error is not a diagnosis by itself. It is a sign that one link in the request chain failed. Follow that chain one hop at a time and the useful error is normally waiting in the next log.