Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Automation By James Joyner IV · · 7 min read

curl Error: '(7) Failed to connect ... Connection refused' — Cause, Fix, and Troubleshooting Guide

Quick answer

Fix curl: (7) Failed to connect to api.example.com port 443: Connection refused — DNS resolved but TCP connect failed: down service, wrong port, firewall.

  • #automation
  • #troubleshooting
  • #curl
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

Overview

curl exit code 7 means the name resolved to an IP, curl opened a TCP connection to that IP:port, and the connection did not complete. Unlike code 6 (DNS), curl knows where to go; the problem is at the transport layer. The message includes the reason and how long it took:

$ curl -v https://api.example.com/health
*   Trying 10.0.4.21:443...
* connect to 10.0.4.21 port 443 failed: Connection refused
* Failed to connect to api.example.com port 443 after 2050 ms: Connection refused
curl: (7) Failed to connect to api.example.com port 443 after 2050 ms: Connection refused

Connection refused (immediate RST) means nothing is listening on that port or a firewall is actively rejecting — the same TCP condition that surfaces as ECONNREFUSED in Node.js and other runtimes. It is distinct from a timeout (code 28), where packets are silently dropped and curl hangs before giving up. Refused is fast and definitive; timed-out is slow and ambiguous.

Symptoms

  • curl: (7) Failed to connect to <host> port <port> after N ms: Connection refused.
  • curl prints Trying <ip>... (so DNS worked) then fails fast — usually well under a couple seconds.
  • nc -zv <host> <port> reports Connection refused too.
  • The same host resolves fine (getent hosts returns an IP) — separating this from a code 6 DNS failure.
curl -sS -o /dev/null -w '%{http_code}\n' https://api.example.com/health; echo "exit: $?"
exit: 7

Common Root Causes

1. The target service is down

Nothing is bound to the port because the process crashed or never started.

# On the target host
ss -ltnp | grep ':443' || echo "no listener on 443"
no listener on 443

2. Wrong port

The service is healthy but listens elsewhere; the caller uses the wrong port (often a default assumption after a config change).

LISTEN 0 511 0.0.0.0:8443 users:(("api",pid=904,fd=7))

3. Firewall / security group sending REJECT

A REJECT rule (as opposed to DROP) returns an RST that curl reports as refused, even though the service is up.

sudo iptables -L INPUT -n | grep -E '443|REJECT'
REJECT  tcp  --  0.0.0.0/0  0.0.0.0/0  tcp dpt:443 reject-with icmp-port-unreachable

4. Service bound to loopback only

Listening on 127.0.0.1 accepts local calls but refuses anything from another host or container.

LISTEN 0 511 127.0.0.1:443 users:(("api",pid=904,fd=7))

5. A proxy is required but not used

The direct route is closed; traffic must go through an HTTP proxy that curl was not told to use.

How to Diagnose

Step 1: Confirm DNS is not the issue

getent hosts api.example.com
10.0.4.21       api.example.com

A resolved IP proves this is code 7 (connect) territory, not code 6 (DNS).

Step 2: Classify refused vs timeout with nc

nc -zv 10.0.4.21 443
nc: connect to 10.0.4.21 port 443 (tcp) failed: Connection refused

Instant “refused” = nothing listening or a REJECT rule. A hang that ends in “timed out” is a different problem (code 28 / DROP).

Step 3: Check for a listener on the target host

ss -ltnp | grep ':443'
systemctl status api --no-pager | head -5
no listener on 443
● api.service - API  Active: failed (Result: exit-code) ...

Step 4: Verify the port and bind address match

ss -ltnp | grep api
LISTEN 0 511 127.0.0.1:8443 users:(("api",pid=904,fd=7))

Here the service is up but on 127.0.0.1:8443 — wrong bind address and wrong port versus a remote caller hitting :443.

Step 5: Rule out a firewall reject

sudo iptables -L INPUT -n | grep -E '443|REJECT'
# Cloud: inspect the security group inbound rules for the port

Fixes

If the service is down, restart it and confirm a listener:

sudo systemctl restart api
ss -ltnp | grep ':443'
LISTEN 0 511 0.0.0.0:443 users:(("api",pid=1201,fd=7))

If the port is wrong, point curl at the actual port:

curl -v https://api.example.com:8443/health

If it is bound to loopback, rebind to 0.0.0.0 (or the routable interface) and restart. If a firewall rejects, open the port in iptables or the security group. If a proxy is required, pass it explicitly:

curl -v --proxy http://proxy.example.com:3128 https://api.example.com/health

A completed connection returns a status line:

< HTTP/1.1 200 OK

What to Watch Out For

  • Refused (code 7) is immediate; timed-out (code 28) hangs — do not apply a firewall-DROP fix to a refused error or vice-versa.
  • The after N ms in the message is connect latency, not a timeout you configured; a small number confirms fast refusal.
  • A service on 127.0.0.1 passes a local curl test but refuses every remote caller — always test from where the automation actually runs.
  • REJECT firewall rules masquerade as “service down”; check iptables/security groups before restarting a healthy service.
  • This is the curl-side view of the same TCP RST that appears as ECONNREFUSED in webhook and job runtimes.
Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.