Fixing Nginx (111: Connection refused) while connecting to upstream

beginner Nginx2026-06-04| Linux (Ubuntu/CentOS/Debian), Nginx, Backend (Node.js, Python, Go, Docker)

Error Message

connect() failed (111: Connection refused) while connecting to upstream, client: 1.2.3.4, server: example.com, upstream: "http://127.0.0.1:3000/"
#nginx#upstream#connection-refused#troubleshooting#devops

The ProblemYou check your Nginx error logs at /var/log/nginx/error.log and find a repeating line that looks like this:

connect() failed (111: Connection refused) while connecting to upstream, client: 1.2.3.4, server: example.com, upstream: "http://127.0.0.1:3000/"

In plain English, Nginx is knocking on the door, but nobody is home. This isn't like a 504 Gateway Timeout where the backend is just slow. A 111 error is an immediate rejection. Either nothing is listening on that port, or the operating system is actively slamming the door shut.

Common Causes- Crashed Service: Your Node.js, PM2, or Gunicorn process died unexpectedly or never started.- Port Confusion: The application is running on port 8080, but Nginx is looking for it on 3000.- The Localhost Trap: Nginx tries to connect via localhost (IPv6 ::1), but your app only listens on 127.0.0.1 (IPv4).- Security Walls: SELinux or a firewall is blocking the internal communication between Nginx and your app.## Step-by-Step Fix### 1. Is the Backend Actually Awake?Before touching any config files, see if your application process is alive. If it crashed, Nginx has nothing to talk to.

# Check systemd status for your app
sudo systemctl status my-backend-app

# Using PM2?
pm2 status

# The old-fashioned way
ps aux | grep node

If the status says inactive or failed, restart it. Check your application logs immediately to see if a syntax error or missing environment variable caused the crash.

2. Verify the Listening PortSometimes a service is "active" but isn't actually bound to the port you expect. Use the ss command to see exactly what is happening on your network stack.

sudo ss -tlpn | grep :3000

You want to see a line like this:

LISTEN 0 128 127.0.0.1:3000 0.0.0.0:* users:(("node",pid=1234,fd=19))

Zero results? Your app isn't listening on port 3000. Look for a PORT=3000 setting in your .env file or hardcoded in your server setup.

3. Audit Your Nginx ConfigOpen your site configuration, usually found in /etc/nginx/sites-available/. Look closely at the proxy_pass line.

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
}

Avoid using "localhost". On modern Linux systems, localhost often resolves to the IPv6 address ::1. If your backend only binds to IPv4 (127.0.0.1), Nginx will fail to connect. Using the explicit IP address 127.0.0.1 is a safer bet that avoids this DNS ambiguity. Test and apply your changes:

sudo nginx -t
sudo systemctl reload nginx

4. Taming SELinux (RHEL/CentOS/AlmaLinux)On Red Hat-based systems, SELinux is often the invisible wall. It defaults to preventing Nginx from making outward network connections.

Check the current setting:

getsebool httpd_can_network_connect

If it returns off, that is your problem. Turn it on permanently with this command:

sudo setsebool -P httpd_can_network_connect 1

Final VerificationMake sure Nginx and your backend are speaking the same language. Run a quick test from the server's command line.

# Can the server talk to the backend?
curl -I http://127.0.0.1:3000

# Can Nginx reach the backend?
curl -I http://localhost

If the first command gives you a 200 OK but the second fails, your Nginx proxy_pass logic is still wrong. If both work, you've solved it.

Docker NoteRunning Nginx in a container? Remember that 127.0.0.1 inside a container refers to that specific container, not your host. If your backend is on the host machine, use host.docker.internal or the host's actual LAN IP address instead.

Related Error Notes