Networking

Test whether a TCP port is reachable

Check from another machine whether a port accepts connections, without telnet. The way a test fails, refused or timed out, tells you whether to look at the service or at a firewall.

Written againstDebian 12Ubuntu 24.04RHEL 9netcat-openbsd 1.219+nmap-ncat 7.92curl 7.76+
Reading time
4 min
Commands to copy
19
Last checked
24 Oct 2026

A TCP port is reachable when the three-way handshake completes: your machine sends a SYN, the server answers with a SYN-ACK. Every tool below does that one thing and reports how it went. Run them from the machine that has the problem, since a port can be open to one network and closed to another.

Test the port with nc

-z connects and closes again without sending data, -v prints the result, and -w 3 gives up after 3 seconds. Debian and Ubuntu install netcat-openbsd:

shell
$ nc -zv -w 3 db.example.net 5432Connection to db.example.net (203.0.113.10) 5432 port [tcp/postgresql] succeeded!$ nc -zv -w 3 db.example.net 5433nc: connect to db.example.net (203.0.113.10) port 5433 (tcp) failed: Connection refused$ nc -zv -w 3 db.example.net 6379nc: connect to db.example.net (203.0.113.10) port 6379 (tcp) timed out: Operation now in progress

RHEL 9 ships Ncat from the nmap project as nc (package nmap-ncat). It takes the same options and words the result differently:

shell
$ nc -zv -w 3 db.example.net 5432Ncat: Version 7.92 ( https://nmap.org/ncat )Ncat: Connected to 203.0.113.10:5432.Ncat: 0 bytes sent, 0 bytes received in 0.02 seconds.$ nc -zv -w 3 db.example.net 6379Ncat: Version 7.92 ( https://nmap.org/ncat )Ncat: TIMEOUT.

Both exit with 0 when the connection succeeded and 1 when it didn’t, so they work in scripts:

shell
$ nc -z -w 3 db.example.net 5432 && echo open || echo closedopen

Without nc installed

Bash can open a TCP connection itself through the path /dev/tcp/host/port. It only works in bash, not in sh or dash, and needs timeout to stop it waiting on a port that doesn’t answer:

shell
$ timeout 3 bash -c '</dev/tcp/db.example.net/5432' && echo open || echo closedopen

curl is installed on most servers and prints each step. With the telnet:// scheme it connects and waits for you to type, so any port works. Press Ctrl+C once it says Connected.

shell
$ curl -v --connect-timeout 3 telnet://db.example.net:5432*   Trying 203.0.113.10:5432...* Connected to db.example.net (203.0.113.10) port 5432

Ports that speak TLS

For HTTPS and other TLS ports, a completed handshake only proves the first step. Make a real request, so you see the TLS handshake and the server’s answer too:

shell
$ curl -sv -o /dev/null https://app.example.net/$ openssl s_client -connect app.example.net:443 -servername app.example.net </dev/null

What the certificate in that output says, and how to read it, is in the article on checking a certificate with openssl.

What the result tells you

ResultWhat happenedLook next at
succeeded, ConnectedThe handshake completed. Something listens, and nothing on the way blocks it.The application and its log
Connection refusedThe host answered with a reset. Nothing listens on that port, or a firewall rule rejects with a TCP reset.ss -tlnp on the server, then its firewall
timed out, TIMEOUTNo answer at all. A firewall drops the packets, the route is wrong, or the host is down.Firewalls on the way, including your provider’s
No route to hostAn ICMP “host unreachable” came back. firewalld sends this for traffic its zone doesn’t allow.firewall-cmd --list-all on the server
Name or service not knownThe name didn’t resolve. Nothing was sent.The name with dig

Test from both ends

If the test fails from outside, run it on the server itself, first against 127.0.0.1 and then against the address clients use:

shell
$ nc -zv -w 3 127.0.0.1 5432$ nc -zv -w 3 10.0.0.5 5432

If the first one succeeds and the second is refused, the service listens on 127.0.0.1 only. ss shows the address it’s bound to (Find which process is listening on a port). If both succeed on the server but the test from outside times out, a firewall in between drops the traffic.

Check the server’s firewall

Each distribution has its own front end. They all end up as nftables rules in the kernel.

shell
# Debian 12: nftables directly$ sudo nft list ruleset# Ubuntu 24.04: ufw$ sudo ufw status verbose# RHEL 9: firewalld$ sudo firewall-cmd --list-all

Open the port to the network that needs it, rather than to everyone:

shell
$ sudo ufw allow from 10.0.0.0/24 to any port 5432 proto tcp$ sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" port port="5432" protocol="tcp" accept'$ sudo firewall-cmd --reload

UDP ports need their own client

UDP has no handshake, so a connection test can’t prove anything. nc -zu reports success when no answer comes back, which is also what happens when a firewall drops the packet. Test a UDP service with a client that speaks its protocol, for example dig @203.0.113.10 example.net for DNS on port 53.

From a Windows machine

PowerShell
PS> Test-NetConnection db.example.net -Port 5432ComputerName     : db.example.netRemoteAddress    : 203.0.113.10RemotePort       : 5432InterfaceAlias   : EthernetSourceAddress    : 10.0.0.21TcpTestSucceeded : True

More PowerShell checks are in the Windows Server category.

Last checked on 24 October 2026 against Debian 12, Ubuntu 24.04, RHEL 9, netcat-openbsd 1.219+, nmap-ncat 7.92, and curl 7.76+. If a step doesn’t match what you see, contact us with the command and what it printed.