Nginx reverse proxy changes "Connection: keep-alive" from upstream to "Connection: close" to client

Hi everyone,

I’m facing an issue with nginx as a reverse proxy. The client sends a POST request with Connection: keep-alive. The upstream backend (a custom HTTP service) responds with HTTP/1.1 200 OK and Connection: keep-alive (I verified this via tcpdump). However, nginx changes the response header to Connection: close before sending it back to the client, and then closes the TCP connection immediately.

This breaks connection reuse – the client has to establish a new TCP connection for every subsequent upload, which hurts performance and may break workflows that rely on a persistent connection.

Environment:

  • nginx version: (please fill in your version, e.g. 1.24.0)
  • OS: (e.g. Ubuntu 22.04, running inside Docker container)
  • Backend: (e.g. custom HTTP server on 172.18.0.3:9006)

Observed behavior (from tcpdump):

  • Client → nginx: POST /file/upload HTTP/1.1 with Connection: keep-alive
  • nginx → backend: same request, added X-Real-IP, still Connection: keep-alive
  • backend → nginx: HTTP/1.1 200 OK with Connection: keep-alive and Content-Length
  • nginx → client: HTTP/1.1 200 OK with Connection: close (!!) and then sends FIN

Partial tcpdump log (annotated):

# Backend response to nginx (shows keep-alive)
20:46:24.019905 IP 172.18.0.3.9006 > 172.18.0.4.40422: Flags [P.], seq 46353:46492, ack 7390962, win 516, length 139
HTTP/1.1 200 OK
Content-Length:78
Connection: keep-alive

# nginx response to client (shows close)
20:46:24.019975 IP 172.18.0.4.9006 > 192.168.228.1.51015: Flags [P.], seq 199866:200061, ack 22151166, win 384, length 195
HTTP/1.1 200 OK
Server: nginx/1.29.8
Connection: close

nginx configuration (relevant part):
(Please paste your actual config, especially the location block that proxies to the backend. If you don’t want to share the full config, at least include these directives: proxy_pass, proxy_http_version, proxy_set_header Connection, keepalive_timeout, and any upstream block.)

Here is what I currently have (example – replace with yours):

location /file/upload {
    proxy_pass http://backend_upstream;
    proxy_http_version 1.1;          # I have this set (or not?)
    proxy_set_header Connection "";   # Not set currently
    proxy_set_header X-Real-IP $remote_addr;
}
upstream backend_upstream {
    server 172.18.0.3:9006;
    keepalive 32;
}

What I have tried:

  • Confirmed that the backend correctly returns Connection: keep-alive
  • Checked that keepalive_timeout is not 0 (it’s 65)
  • Verified nginx version: (your version)
  • I do NOT have proxy_set_header Connection close anywhere

Expected behavior:
nginx should preserve the upstream’s Connection: keep-alive (or at least not force close) so that the client can reuse the same TCP connection for multiple requests.

Questions:

  1. Why does nginx override the Connection header from upstream?
  2. What configuration changes are required to make nginx keep the connection alive to the client?
  3. Is there any implicit behavior (e.g., proxy_http_version 1.0 default) that forces nginx to add Connection: close even when upstream uses 1.1?

Any help would be greatly appreciated. Thanks!