Illustrative Nginx lesson with an engineer using a laptop beside a client–Nginx–application diagram.

WIUNIX ACADEMY

Nginx 502 and 504: identify the failing upstream phase

Separate connection refusal, upstream queueing, slow response headers and proxy timeouts using bounded probes and correlated timing.

Advanced30 min readUpdated
Make this guide yours

YOUR SYSTEM. YOUR STEPS.

Where are you working?

Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.

AlmaLinux 10Reviewed against official documentation ·

USE YOUR OWN VALUES

Configure this tutorial

Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.

Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.

Set values for my environment5 editable values

THE OUTCOME

What will you have at the end?

Separate connection refusal, upstream queueing, slow response headers and proxy timeouts using bounded probes and correlated timing.

Supported operating systems and versions

Primary-documentation review; no production execution or universal installation test is claimed. Examples assume existing tools on Linux and must be adapted to authorized deployment context.

Before you begin

  • Authorized operator access, existing tools named in the panels, and a private place to retain diagnostic evidence.
  • Use a staging environment or an approved read-only production investigation; review disruptive changes separately.

A status code is a symptom, not a root cause

A 502 usually means the gateway could not obtain a usable upstream response; a 504 means its wait expired. Neither tells you whether the fault is a refused TCP connection, an overloaded worker pool, an invalid protocol response or a deadline imposed by another proxy. Work on one known read-only endpoint and one UTC time window. This guide assumes host Nginx proxies plain HTTP to one known address. FastCGI, gRPC, Unix sockets and a container-only network need their own transport checks.

01

LET’S BUILD

Check the selected distribution and required tools first

Run on the authorized diagnostic host or disposable lab VM. Compare the installed OS and required packages below with the selected path. Obtain missing tools from the distribution’s enabled official repositories through your approved package-management process; this workflow does not reinstall an existing server or switch its repository.

Use an entitled RHEL repository or the matching official Rocky Linux/AlmaLinux repositories and a currently supported minor release. SELinux and system crypto policy remain enforced; do not weaken them to make a diagnostic pass.

An existing Nginx installation is required. Packaged deployments normally use nginx.service and nginx.conf under /etc/nginx, but discover active includes and logs from the effective configuration. Do not assume Ubuntu sites-enabled layout on an RPM host.

Effective configuration can contain private deployment data; keep it private. Set this guide’s error-log field to the actual configured destination.

Inspect the curl executable and its owning package; a valid curl-minimal installation need not be replaced by full curl. Confirm HTTP/HTTPS support in the installed build’s version output.

bash

Authorized terminal on the host identified in this section

cat /etc/os-release
rpm -q iproute
command -v curl ss nginx
sudo nginx -t
sudo nginx -T 2>&1 | sed -n '1,100p'
systemctl show nginx.service -p FragmentPath -p DropInPaths -p ExecStart
if command -v curl >/dev/null 2>&1; then
  rpm -qf "$(command -v curl)"
else
  printf '%s\n' 'Required curl executable is missing.'
  exit 1
fi
curl --version
How do I know it worked?

Required packages and tools are identified; resolve missing tools before continuing.

02

LET’S BUILD

Record versions and the exact endpoint

On the proxy host, record time and Nginx build, then request the authorized public hostname without credentials or query parameters. The response header timing includes DNS, TCP and TLS effects on this path; it is not an application-only measurement. Use an existing harmless root endpoint, or adapt the URL to an approved health route before running.

bash

Authorized Linux terminal

date -u +'%Y-%m-%dT%H:%M:%SZ'
nginx -v
curl --noproxy '*' --connect-timeout 3 --max-time 15 -sS -o /dev/null -w 'status=%{http_code} connect=%{time_connect} tls=%{time_appconnect} first_byte=%{time_starttransfer} total=%{time_total}\n' 'https://app.example.com/'
How do I know it worked?

Record the HTTP status or curl transport error with the UTC timestamp. A timeout at 15 seconds is the probe deadline, not proof of Nginx’s own timeout.

03

LET’S BUILD

Check host policy independently

Correlate a denial with the exact process, path and failure time. An empty result or a failed audit query is not proof that SELinux is uninvolved. Inspect labels, process domain and installed policy; this step generates no allow policy and does not disable enforcement.Shell curl tests that caller’s path, while Nginx can run in a different SELinux domain. Inspect httpd_can_network_connect alongside the AVC. A global boolean change or public firewall opening is not an automatic502 repair; any policy change needs owner approval, bounded scope and rollback. An inactive firewalld does not prove absence of other filtering.

bash

Authorized terminal on the host identified in this section

getenforce
sudo ausearch -m AVC,USER_AVC -ts recent
ps -eZ | grep '[n]ginx'
getsebool httpd_can_network_connect
sudo firewall-cmd --state
sudo firewall-cmd --get-active-zones
How do I know it worked?

Any suspected policy failure is tied to specific evidence; enforcement and firewall configuration remain unchanged.

04

LET’S BUILD

Bypass the proxy without changing exposure

From the Nginx host, call the existing private backend with the same Host value. This is an authorized read-only probe, not permission to publish a private port. The HTTP Host may select a virtual application; HTTPS upstreams require verified TLS/SNI instead of this plain-HTTP example.

bash

Authorized Linux terminal

curl --noproxy '*' --connect-timeout 3 --max-time 15 -H 'Host: app.example.com' -sS -o /dev/null -w 'status=%{http_code} connect=%{time_connect} first_byte=%{time_starttransfer} total=%{time_total}\n' 'http://127.0.0.1:8000/'
How do I know it worked?

A repeatable private response establishes a comparison. Immediate refusal differs from a connection timeout and from a slow first byte.

05

LET’S BUILD

Separate a missing listener from a saturated application

On the upstream host, inspect listeners and the application unit. The port must match the actual proxy configuration; a process listening only in another network namespace is not a host listener. A unit marked active can still serve errors or queue requests. Avoid repeatedly restarting the process before collecting its recent failure evidence.

bash

Authorized Linux terminal

sudo ss -lntp 'sport = :8000'
sudo systemctl status 'example-app.service' --no-pager
sudo journalctl -u 'example-app.service' --since '-10 min' --no-pager -n 100
How do I know it worked?

Match the exact PID/address/port and recent application errors. Logs may contain private values; review locally and redact before sharing.

Explore all tutorials ↗