ERROR

How to Fix Nginx: Connection Refused While Connecting to Upstream

Quick Fix Summary

TL;DR

Check if your upstream application server (e.g., Node.js, Gunicorn) is running and listening on the correct port and interface.

Nginx cannot establish a TCP connection to the backend server defined in the `upstream` block or `proxy_pass` directive. This is a network-level failure, not an application error.

Diagnosis & Causes

  • Upstream application server is not running.
  • Firewall or security group blocking the port.
  • Upstream server listening on wrong IP (127.0.0.1 vs 0.0.0.0).
  • Incorrect port number in Nginx upstream configuration.
  • Resource exhaustion (e.g., out of memory) on the upstream host.
  • Recovery Steps

    1

    Step 1: Verify Upstream Server Status

    First, confirm the backend process is alive and bound to the expected port.

    bash
    # Check if the service is running (e.g., for a systemd service)
    sudo systemctl status your-app-service
    # Or, check for listening ports on the upstream server
    sudo ss -tlnp | grep :<your-port>
    # Example: Check for a process on port 3000
    sudo ss -tlnp | grep :3000
    2

    Step 2: Test Network Connectivity from Nginx Host

    Rule out basic network issues by testing the connection from the Nginx server to the upstream.

    bash
    # Use telnet or netcat to test the TCP connection
    telnet <upstream-server-ip> <upstream-port>
    # Or with netcat (nc)
    nc -zv <upstream-server-ip> <upstream-port>
    # Example: Test connection to 10.0.1.5 on port 8080
    nc -zv 10.0.1.5 8080
    3

    Step 3: Inspect Firewall and Security Groups

    Ensure no firewall (iptables, nftables, cloud security group) is blocking the traffic.

    bash
    # Check iptables rules on the UPSTREAM server
    sudo iptables -L -n -v | grep <nginx-server-ip>
    # Check if the port is allowed in the INPUT chain
    sudo iptables -L INPUT -n -v
    # For AWS/Azure/GCP, verify the Security Group/NSG allows traffic from Nginx's IP on the upstream port.
    4

    Step 4: Verify Upstream Binding Interface

    A common mistake is the app binding only to localhost (127.0.0.1), making it unreachable from Nginx on another host.

    bash
    # On the UPSTREAM server, check what IP your app is listening on.
    # Look for 0.0.0.0 (all interfaces) or a specific IP. 127.0.0.1 is wrong for remote Nginx.
    sudo netstat -tlnp || sudo ss -tlnp
    # Example output showing problem:
    # tcp   0   0 127.0.0.1:3000    0.0.0.0:*   LISTEN  12345/node
    # Fix: Configure your app (e.g., Node.js, Flask, Gunicorn) to listen on '0.0.0.0'.
    5

    Step 5: Audit Nginx Upstream Configuration

    Ensure the `upstream` block or `proxy_pass` directive points to the correct server and port.

    nginx
    # Check your Nginx config for the upstream definition
    sudo nginx -T | grep -A 10 -B 2 "upstream"
    sudo nginx -T | grep -B 5 -A 5 "proxy_pass"
    # Example correct configuration:
    upstream backend {
        server 10.0.1.5:8080;
    }
    server {
        location / {
            proxy_pass http://backend;
        }
    }
    6

    Step 6: Check Nginx Error Logs for Specifics

    Nginx error logs provide the exact reason for the connection refusal. Tail them in real-time while reproducing the error.

    bash
    # Tail the Nginx error log (common paths)
    sudo tail -f /var/log/nginx/error.log
    sudo tail -f /var/log/nginx/*.error.log
    # Look for entries like:
    # connect() failed (111: Connection refused) while connecting to upstream

    Architect's Pro Tip

    "When using Docker, ensure your app container's port is published (`-p 8080:8080`) and the app inside listens on `0.0.0.0`, not `127.0.0.1`. Nginx outside Docker must use the host's IP, not `container_name`."

    Frequently Asked Questions

    My upstream server is on the same host as Nginx. Why do I get 'Connection Refused'?

    This is almost always because your application server (e.g., Gunicorn, Node) is binding to `127.0.0.1` (localhost). Nginx connects via the loopback interface, but if the app only listens on a specific IP (like `127.0.0.1`), it works. However, if Nginx is configured to use the host's public IP or `localhost` resolves differently, it fails. The fix is to bind your app to `0.0.0.0` to listen on all network interfaces.

    I fixed the issue, but Nginx still returns 502 Bad Gateway. What's wrong?

    Nginx may be caching the failed upstream state. After confirming your upstream is healthy (using `nc -zv`), reload Nginx to clear its upstream connection cache: `sudo systemctl reload nginx`. Also, check the `proxy_next_upstream` and `proxy_connect_timeout` directives if the failure was intermittent.

    What's the difference between 'Connection refused' and 'No route to host'?

    'Connection refused' (ECONNREFUSED) means the TCP SYN packet reached the host, but nothing was listening on the target port. 'No route to host' (EHOSTUNREACH) is a lower-level network error where the host's IP address is not reachable at all, typically due to routing or firewall issues before the packet even arrives.

    Related Nginx Guides