Fix Connection Refused on Linux

Tested on RHEL 10.2 (Coughlan) — vm1.lab.example (192.168.56.116) as server; client checks run against that IP from the same LAN
Package iproute 6.17.0-2.el10
httpd 2.4.63-13.el10_2.5
firewalld 2.4.3-2.el10_2
nmap-ncat 7.92-5.el10
curl 8.12.1
Applies to Ubuntu, Debian, Kali Linux, Linux Mint, Pop!_OS, Raspberry Pi OS, elementary OS, Zorin OS, Parrot OS, MX Linux, RHEL, Rocky Linux, AlmaLinux, Oracle Linux, CentOS Stream, Fedora, Arch Linux
Privilege Normal user on the client for diagnostics; sudo or root on the server for systemctl, ss, and firewall changes
Scope Diagnose connection refused for any TCP client — ssh, curl, web ports, custom services — using ping, systemd, ss, bind-address checks, and firewalld. Contrasts refused with timeout and no route to host. Does not replace SSH key or sshd_config authentication troubleshooting.
Related guides No route to host
Network is unreachable
Test port connectivity
firewalld cheat sheet
curl command

curl http://192.168.56.116 dies with Connection refused while ping 192.168.56.116 still gets replies. The host is up and routed — the failure is on the TCP port: nothing accepted the connection, or the daemon listens somewhere your client cannot reach.

Work from the client to confirm reachability, then on the server check the service, listening sockets, bind address, and firewall before you retest with nc or curl.


What connection refused means

Linux maps ECONNREFUSED to connection refused on connect(). The packet reached the destination IP; the kernel or a listener sent TCP RST because no process accepted that port.

Tool Typical message
ssh connect to host … port 22: Connection refused
curl Failed to connect … Connection refused (curl error 7)
bash /dev/tcp/… connect: Connection refused
nc Ncat: Connection refused
ping Usually succeeds — ICMP is not the same port check

Use a literal IP first so DNS stays out of the picture. If only hostnames fail while IP ping works, see Linux can ping IP but not hostname.

IMPORTANT
Connection refused is not a routing-table problem. When ip route get answers Network is unreachable, fix routes first — that is network is unreachable. When the client prints no route to host while ping works, inspect firewall REJECT rules in no route to host before you restart services.

Confirm the server is reachable

Ping the server IP from the client. Skip hostnames so you separate L3 reachability from DNS:

bash
ping -c2 -W2 192.168.56.116
output
PING 192.168.56.116 (192.168.56.116) 56(84) bytes of data.
64 bytes from 192.168.56.116: icmp_seq=1 ttl=64 time=0.056 ms
64 bytes from 192.168.56.116: icmp_seq=2 ttl=64 time=0.072 ms

--- 192.168.56.116 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1035ms

Replies mean the host answers at the IP layer. That does not prove any TCP port is open.

Confirm the kernel would route to that address the same way your application does:

bash
ip route get 192.168.56.116
output
local 192.168.56.116 dev lo src 192.168.56.116 uid 0
    cache <local>

A dev or local line means a route exists. If this command fails with Network is unreachable, stop here and fix routing before chasing daemons.


Check whether the service is running

Connection refused on port 80 often means the web server is stopped even though SSH on port 22 still works. On the server, check the unit you expect to own the port — here httpd for HTTP:

bash
sudo systemctl status httpd --no-pager

When the service is healthy, the active line shows running:

output
● httpd.service - The Apache HTTP Server
     Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; preset: disabled)
     Active: active (running) since Sat 2026-08-22 08:55:14 IST; 2min ago

Stop the service to reproduce the error clients see:

bash
sudo systemctl stop httpd

From the client, retry HTTP against the same IP:

bash
curl -v --connect-timeout 3 http://192.168.56.116/
output
*   Trying 192.168.56.116:80...
* connect to 192.168.56.116 port 80 from 192.168.56.116 port 42818 failed: Connection refused
* Failed to connect to 192.168.56.116 port 80 after 0 ms: Could not connect to server
curl: (7) Failed to connect to 192.168.56.116 port 80 after 0 ms: Could not connect to server

Immediate refusal with ping still working points at a stopped or crashed daemon — not cable loss. Bring the unit back before you move on:

bash
sudo systemctl start httpd

systemctl start exits silently when the unit enters active (running); confirm with systemctl is-active httpd, which should print active.

The same pattern applies to sshd on port 22: stopping sshd makes ssh print connection refused even when the host still answers ping.


Check listening ports with ss

ss shows which addresses and ports have listeners. On the server, filter for the port your client uses:

bash
ss -tlnp | grep ':80'

With httpd running, Apache binds all interfaces on port 80:

output
LISTEN 0      511                                     *:80               *:*    users:(("httpd",pid=14951,fd=4),("httpd",pid=14953,fd=4),("httpd",pid=14955,fd=4),("httpd",pid=14957,fd=4))

The *:80 local address means any IPv4 client can connect when the firewall allows it. No line for that port while the service claims to be up means the process has not opened the socket yet — check logs with journalctl -u httpd -e.

For SSH, confirm port 22 is listening:

bash
ss -tlnp | grep ':22'
output
LISTEN 0      128                               0.0.0.0:22        0.0.0.0:*    users:(("sshd",pid=15217,fd=7))
LISTEN 0      128                                  [::]:22           [::]:*    users:(("sshd",pid=15217,fd=8))

If clients target port 2222 but ss only shows :22, fix the client port or change Port in sshd_config and restart sshd.


Check the service bind address

A listener on 127.0.0.1 accepts only local connections. Remote clients hit the host IP and get connection refused even when a process owns the port locally.

Open port 8888 in the firewall first so filtering does not mask the bind issue:

bash
sudo firewall-cmd --permanent --add-port=8888/tcp

Reload firewalld so the rule is active:

bash
sudo firewall-cmd --reload

Start a test listener on loopback only:

bash
nc -l 127.0.0.1 8888 >/dev/null &

Verify the socket is loopback-scoped:

bash
ss -tlnp | grep ':8888'
output
LISTEN 0      10                              127.0.0.1:8888       0.0.0.0:*    users:(("nc",pid=15147,fd=5))

Local clients can connect to loopback:

bash
timeout 3 bash -c '</dev/tcp/127.0.0.1/8888' && echo open
output
open

The same port on the LAN IP is refused because nothing listens on that address:

bash
timeout 3 bash -c '</dev/tcp/192.168.56.116/8888' 2>&1
output
bash: connect: Connection refused
bash: line 1: /dev/tcp/192.168.56.116/8888: Connection refused

Fix ListenAddress in the service config — for SSH, /etc/ssh/sshd_config should use 0.0.0.0 or the specific NIC IP, not only 127.0.0.1. Remove the test port and stop the background nc when you are done:

bash
sudo firewall-cmd --permanent --remove-port=8888/tcp

Reload firewalld so the removal takes effect:

bash
sudo firewall-cmd --reload

Check firewall rules

Firewalls can block before traffic reaches a listener. On RHEL-family systems, list the active zone on the server:

bash
sudo firewall-cmd --list-all

The services and ports lines show what is allowed. HTTP and SSH are already open in this lab zone:

output
services: cockpit dhcp dhcpv6-client dns http https nfs ssh tftp
  ports: 1313/tcp

A DROP rule drops packets without RST, which clients usually report as timeout — not refused. Add a direct DROP on port 9443 to demonstrate the difference:

bash
sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 9443 -j DROP

Apply the DROP rule:

bash
sudo firewall-cmd --reload

From the client, a short timeout on /dev/tcp hangs until it expires:

bash
timeout 5 bash -c '</dev/tcp/192.168.56.116/9443' 2>&1; echo exit=$?
output
exit=124

exit=124 means the timeout command ended the wait — classic silent DROP behavior.

Remove the test DROP rule when finished:

bash
sudo firewall-cmd --permanent --direct --remove-rule ipv4 filter INPUT 0 -p tcp --dport 9443 -j DROP

Reload again so the DROP rule disappears:

bash
sudo firewall-cmd --reload

On this host, blocked ports without a listener can also return connection refused immediately. When ping works but TCP fails with no route to host instead, read no route to host for REJECT rich rules. Opening the firewall does not start the service — you still need a listener in ss.


Verify the port with nc or curl

After the service is running and the firewall allows the port, confirm from the client with a one-shot TCP check:

bash
timeout 3 bash -c '</dev/tcp/192.168.56.116/80' && echo open
output
open

nc prints an explicit result for the same test:

bash
nc -zv -w3 192.168.56.116 80
output
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Connected to 192.168.56.116:80.
Ncat: 0 bytes sent, 0 bytes received in 0.02 seconds.

curl exercises the application layer when HTTP or HTTPS is in scope:

bash
curl -sS -o /dev/null -w 'http_code=%{http_code}\n' --connect-timeout 3 http://192.168.56.116/
output
http_code=403

HTTP 403 means TCP connected and Apache answered — connection refused is gone. The status code itself is a separate authorization or index issue.

For a port that is allowed through the firewall but has no daemon, the client still gets refused:

bash
sudo firewall-cmd --permanent --add-port=59999/tcp

Reload so the open port is live before you probe it:

bash
sudo firewall-cmd --reload

With the firewall open and no listener, nc should report refused immediately:

bash
nc -zv -w3 192.168.56.116 59999 2>&1
output
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Connection refused.

Clean up the test port:

bash
sudo firewall-cmd --permanent --remove-port=59999/tcp

Reload to drop the temporary test port:

bash
sudo firewall-cmd --reload

Connection refused vs timeout

Both errors happen before application data flows, but the fix differs:

Message errno / behavior Ping to same IP Typical cause
Connection refused ECONNREFUSED, immediate RST Often works Service down, wrong port, bind to 127.0.0.1 only
Connection timed out No TCP reply until timer May work Firewall DROP, filtered path, wrong IP
No route to host EHOSTUNREACH Often works Firewall REJECT with ICMP unreachable — see no route to host
Network is unreachable ENETUNREACH Fails for remote targets Missing default route — see network is unreachable

Demonstration on the same server IP with httpd stopped:

bash
timeout 3 bash -c '</dev/tcp/192.168.56.116/80' 2>&1
output
bash: connect: Connection refused
bash: line 1: /dev/tcp/192.168.56.116/80: Connection refused

With a DROP rule on port 9443, the same client waits until timeout kills the attempt — compare that delay with the instant RST above.

Wrong SSH port while sshd listens on 22 produces the same immediate refusal:

bash
ssh -o ConnectTimeout=3 -p 2222 root@192.168.56.116 exit 2>&1
output
ssh: connect to host 192.168.56.116 port 2222: Connection refused

Troubleshooting

Symptom Likely cause Fix
Ping OK; TCP refused on one port Service stopped or wrong port systemctl start unit; match client port to ss -tlnp
Ping OK; refused only from remote clients Listener on 127.0.0.1 Set ListenAddress 0.0.0.0 or NIC IP; restart daemon
ss shows *:port but remote refused Firewall or SELinux firewall-cmd --add-service or --add-port; check ausearch if enforcing
Refused on every port including SSH sshd down or host is wrong IP systemctl start sshd; verify target IP with ip addr on server
Hangs then timeout, not refused DROP rule or dead path nft list ruleset | grep -i drop; compare with test port connectivity
No route to host (not refused) REJECT with ICMP Allow port in firewalld; see no route to host
ip route get → Network is unreachable Missing route Fix default gateway / routes
Hostname fails; IP works DNS, not TCP port Linux ping IP but not hostname

References


Summary

Connection refused means the packet reached the host and nothing accepted your TCP port — ECONNREFUSED at the socket layer. Ping can succeed while curl, ssh, or nc fail because ICMP does not prove a listener exists on port 80, 443, or 22.

Start on the client with ping and ip route get so you know routing is not the blocker. On the server, match systemctl status and ss -tlnp to the port your client uses, then check whether the daemon binds only to 127.0.0.1. Open the port in firewalld or nftables when filtering is in play, and retest with bash /dev/tcp, nc -zv, or curl -v.

Instant refusal with a live ping usually means start the service or fix the bind address. A long hang points at DROP or network loss — not the same fix. When the message is no route to host or network is unreachable instead, switch to the sibling guides for firewall REJECT and missing routes before you chase application logs.


Frequently Asked Questions

1. What does connection refused mean on Linux?

The TCP packet reached the destination host and the kernel sent RST back because nothing accepted the connection on that port. Routing worked; the service is down, listening on another address, or bound to a different port.

2. Why does ping work but curl shows connection refused?

Ping uses ICMP. curl opens a TCP socket on the port you specify. A live host with no listener on that port returns ECONNREFUSED. Start the service, open the correct port in the firewall, or fix ListenAddress so the daemon binds where clients connect.

3. How is connection refused different from connection timed out?

Refused means an immediate RST from the target or its kernel — often no listener. Timeout means no TCP response at all, typical of firewall DROP rules, wrong IP, or a dead path. Compare with nc -w3 and a short timeout on bash /dev/tcp.

4. Can a firewall cause connection refused?

Sometimes. REJECT rules may return ICMP errors that some clients phrase differently, but a silent DROP usually causes timeout instead. On many hosts an allowed firewall port with no running service still shows connection refused.

5. Which tools show connection refused the same way?

ssh, curl, wget, bash /dev/tcp/host/port, and nc all surface ECONNREFUSED when the handshake gets RST. Use the same IP and port across tools so you are not chasing different errors.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)