In a CDN → load balancer → nginx → application chain, each hop has its own connections and idle timers. If one hop reuses a connection its neighbor already closed, a reset, retry and sometimes 502 can follow. Confirm the mechanism with logs from both neighbors and connection evidence; one client error does not prove it.

HTTP keep-alive allows multiple requests on one connection. A chain on a diagram is actually several independent TCP links: client–CDN, CDN–load balancer, load balancer–nginx and nginx–app. A setting named keepalive_timeout may govern a different side in each product. nginx separately documents client keepalive and its upstream connection cache; do not copy a value between hops blindly.
Map adjacent connections
| Link | Who reuses it | Where to inspect | Record |
|---|---|---|---|
| Client → CDN | Client and CDN | Public edge | HTTP version, idle limit, status |
| CDN → load balancer | CDN | CDN origin pool | Idle age, reset and retry |
| Load balancer → nginx | Load balancer | Backend pool | Close and reconnection |
| nginx → app | nginx when upstream keepalive is enabled | nginx upstream and app server | Which side closed idle first |
Do not simply sort all timeout numbers: products count from different events and apply different retry rules. The practical goal is to avoid keeping a socket available for reuse after its peer considers it closed. The right fix depends on which side retries and whether replaying a request is safe. Be especially careful with POST and other operations with side effects. RFC 9112 defines HTTP/1.1 connection handling.

Map connection owners and idle timers
# On nginx, inspect active settings and logs at the failure timestamp
nginx -T 2>&1 | grep -E 'keepalive|proxy_http_version|proxy_set_header Connection|proxy_read_timeout'
# Outside, capture status and remote address
curl -sS -o /dev/null -w 'status=%{http_code} ip=%{remote_ip} total=%{time_total}\n' https://example.com/
# Proving reuse/reset needs a persistent client or adjacent-hop logs.
Separate curl invocations usually create new connections; they do not reproduce a single connection pool’s reuse. A lab test needs a persistent client or controlled workload with idle gaps. Check nginx proxy_http_version and Connection header behavior in the proxy documentation. If the failure happens after a request was sent, inspect the upstream timeout phase; a read timeout and a reset idle socket are different cases.
An enterno infrastructure observation
During an architecture review on November 11, 2026, a cloud address returned 502 while the corresponding entry was absent from the host’s Traefik error-status log sample. That suggests looking farther out in the chain, but it does not prove a keep-alive mismatch. The Traefik access log recorded only 400–599 responses, and several other explanations remain possible. We therefore do not present the incident as a confirmed idle reset. A request ID and logs from adjacent hops in the same time window would be needed. See the 5xx hop guide.
In production, capture timestamp, request ID, origin address, each hop’s status, and signs such as connection reset by peer or upstream prematurely closed connection. Compare failures with idle gaps and configuration changes. Adjust one timer or reuse policy, then compare the results. Check client waiting time with curl timing and external availability with a site check.
How we made this guide
The nginx behavior is based on official module docs and RFC 9112. The enterno observation comes from an internal architecture note and lacks a full connection trace; it is not used to establish root cause. SerpAPI captured Google US and Yandex Moscow results. Wordstat region 225 returned no estimate for the exact phrase and 25 for keepalive timeout nginx.
FAQ
Is every 502 after an idle period a keep-alive problem?
No. Verify who emitted the status, the reset and both adjacent logs.
Can I disable keep-alive?
It may hide a symptom while increasing TCP/TLS connection load. Confirm the mechanism and measure the effect.
Why is the nginx log clean?
The error may occur upstream of nginx or outside the configured log sample. Check the preceding hop.
Which timeout should be longer?
Identify who retains the socket for reuse and who closes it. Then align adjacent timers with the retry policy.