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.
Make this guide yoursYOUR SYSTEM. YOUR STEPS.
Where are you working?
Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.
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.
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.
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 --versionRequired packages and tools are identified; resolve missing tools before continuing.
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.
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/'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.
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.
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-zonesAny suspected policy failure is tied to specific evidence; enforcement and firewall configuration remain unchanged.
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.
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/'A repeatable private response establishes a comparison. Immediate refusal differs from a connection timeout and from a slow first byte.
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.
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 100Match the exact PID/address/port and recent application errors. Logs may contain private values; review locally and redact before sharing.