To check open ports on Linux, run sudo ss -tulpn. It lists every TCP and UDP port a program is listening on, the address it listens on and the process behind it. Listening is only half the answer, though. A port is reachable from the internet only when three things line up: a process listens on it, it listens on a public address (0.0.0.0 or [::], not 127.0.0.1), and the firewall lets the traffic in. The last check is always a test from another machine.
$ sudo ss -tulpn
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
udp UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=587,fd=14))
tcp LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=587,fd=15))
tcp LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=1021,fd=3))
tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1340,fd=5))
tcp LISTEN 0 151 127.0.0.1:3306 0.0.0.0:* users:(("mysqld",pid=1188,fd=21))
tcp LISTEN 0 4096 [::]:22 [::]:* users:(("sshd",pid=1021,fd=4))
tcp LISTEN 0 511 [::]:80 [::]:* users:(("nginx",pid=1340,fd=6))Read it like this: SSH (22) and nginx (80) listen on all addresses, IPv4 and IPv6, so they are open to the network unless a firewall says otherwise. MySQL (3306) listens on 127.0.0.1 only, so nothing outside the server can reach it, firewall or not. The port 53 entries on 127.0.0.53 are explained below. Which service a port number belongs to, and whether it should face the internet at all, is in the list of common ports.
How do I list listening ports?
ss is part of iproute2 and preinstalled on Debian, Ubuntu, Rocky and Alma. On Alpine, add it with apk add iproute2-ss, or use BusyBox’s netstat -tulpn. The flags spell out what you are asking for:
-t TCP
-u UDP
-l listening sockets only
-p show the process (needs sudo to see other users' processes)
-n numbers, not names: 22 instead of "ssh"
sudo ss -tlnp # TCP only
sudo ss -ulnp # UDP only
sudo ss -tlnp | grep ':443' # is anything on 443?The column that matters is Local Address:Port; the peer column is just * for a listening socket. netstat -tulpn prints the same information, but it belongs to the old net-tools package, which current Debian, Ubuntu and Rocky images no longer install. If you prefer it, see how to install net-tools.
On Ubuntu 24.04, port 22 may show systemd next to or instead of sshd. SSH is socket-activated there: systemd holds the port and starts sshd when someone connects.
What do 0.0.0.0, [::] and 127.0.0.1 mean?
0.0.0.0:80: every IPv4 address of the server, including the public one. Reachable from outside unless the firewall blocks it.[::]:80: every IPv6 address. On Linux such a socket usually accepts IPv4 connections too, unless the program asks for IPv6 only.*:80means the same: all addresses.127.0.0.1:3306or[::1]: loopback only. Only programs on the same server can connect. This is where databases, caches and admin interfaces belong.- A specific address, such as
203.0.113.10:443: only that one address, which matters on servers with several IPs.
127.0.0.53:53 (and 127.0.0.54:53 on newer systems) is the local DNS stub of systemd-resolved. Programs on the server send their DNS lookups there, and it forwards them to the real resolvers. It is normal, it is loopback-only, and it is not an open DNS server.
Which process is using a port?
ss already shows the name and PID. To ask about one port, or to use other tools:
sudo ss -ltnp 'sport = :80' # ss with a filter
sudo lsof -nP -iTCP:80 -sTCP:LISTEN # lsof: listening TCP sockets on 80
sudo lsof -i :80 # every socket using port 80, any state
sudo fuser 80/tcp # just the PID (package psmisc)
systemctl status 1340 # which service owns PID 1340The last line is handy: systemctl status accepts a PID and names the unit it belongs to, so you know what to stop or reconfigure. If you are here because a program cannot start with “address already in use”, the next steps are in Address already in use.
Does the firewall let the traffic in?
A listening port is still closed to the outside if the host firewall drops it. Ask the firewall you use:
sudo ufw status verbose # Ubuntu, Debian with UFW
sudo firewall-cmd --list-all # Rocky, Alma (firewalld)
sudo nft list ruleset # everything, as nftables sees it
sudo iptables -L INPUT -n -v --line-numbers # the classic viewStatus: inactive in UFW and an empty ruleset mean there is no host firewall, and every port listening on a public address is open. The UFW setup guide turns that into a deny-by-default firewall without locking you out of SSH.
How do I check if a port is open from outside?
Test from a different machine, such as your PC or a second server. A test from the server against its own public IP goes through the loopback interface, which most firewalls allow, so it can report a port as open that the internet cannot reach.
# Linux or macOS
nc -zv 203.0.113.10 443
# Connection to 203.0.113.10 443 port [tcp/https] succeeded!
# no nc? bash can open a TCP connection itself
timeout 3 bash -c '</dev/tcp/203.0.113.10/443' && echo open || echo closed
# web services: see the real answer, TLS included
curl -v https://example.com/
# Windows PowerShell
Test-NetConnection 203.0.113.10 -Port 443 # TcpTestSucceeded : True
# several ports at once (your own servers only)
nmap -Pn -p 22,80,443 203.0.113.10- Succeeded / open: something answered; the port is reachable.
- Connection refused / closed: the server answered, but nothing listens on that port, or a firewall rule rejects it.
- Timed out / filtered: no answer at all. A firewall dropped the packets (UFW’s default deny drops silently), the address is wrong, or the server is down.
Point nmap only at machines you run. For authorised security testing of other hosts, read how to run nmap, masscan and zmap from a VPS first, and use a server where that work is allowed, such as Pentesting KVM, which permits scanning within its AUP.
For a machine on your home connection — a game server or a NAS behind your router — the open port checker runs the outside test from our side, against the public IPv4 address you browse from.
Why is a port open locally but closed from outside?
- It listens on 127.0.0.1.
ssshows127.0.0.1:PORT. Change the bind address in the program’s configuration (bind-addressfor MySQL and MariaDB, the host argument of a Node or Python app), restart it, and open the port in the firewall, but only if it really should be public. - The firewall drops it.
ssshows0.0.0.0:PORTand the outside test times out. Add the rule:sudo ufw allow 8080/tcp, orsudo firewall-cmd --permanent --add-port=8080/tcp && sudo firewall-cmd --reload. - IPv4 versus IPv6. A program listening only on IPv6 (
[::]with IPv6-only set, or[::1]) cannot be reached over IPv4, and a client that prefers IPv6 fails when the server has no IPv6 listener or firewall rule. Test each explicitly withnc -4 -zvandnc -6 -zv. - Something between you and the server. Your own network may block outgoing ports; test from another connection, such as a phone hotspot.
From a browser the two firewall outcomes look different: a rejected or unbound port fails at once with ERR_CONNECTION_REFUSED, a dropped one only after a wait with ERR_CONNECTION_TIMED_OUT.
The opposite surprise, a port that is open although UFW should block it, nearly always comes from Docker: published container ports bypass UFW’s rules. The fix is in why container ports ignore UFW.
Why is UDP harder to check?
UDP has no handshake. With TCP, a closed port answers with a refusal; with UDP, an open port often says nothing, and a port behind a firewall says nothing either. A plain probe therefore cannot tell them apart:
nc -zvu 203.0.113.10 51820 # "succeeded" only means no error came back
sudo nmap -sU -p 51820 203.0.113.10 # often reports open|filtered for the same reason
# better: talk the actual protocol
dig @203.0.113.10 example.com # a DNS server on port 53
sudo wg show # WireGuard, on the server: is there a recent handshake?For UDP services, the reliable test is a real client: a DNS query, a game client joining, a VPN completing its handshake.
How do I check open ports on Windows?
netstat -ano | findstr LISTENING # last column is the PID
Get-NetTCPConnection -State Listen | Sort-Object LocalPort |
Format-Table LocalAddress, LocalPort, OwningProcess
Get-NetUDPEndpoint | Sort-Object LocalPort # UDP
Get-Process -Id 1234 # which program is PID 1234To open a port, see opening a port in Windows Firewall. The outside test is the same as for Linux: Test-NetConnection or nc from another machine.
How do I close a port I don’t need?
Stop the program that listens on it. A firewall rule hides a port, but a service that is not running cannot be attacked at all. In order of preference:
# 1. stop and disable the service (and its .socket unit, if it has one)
systemctl status 1340 # find the unit from the PID
sudo systemctl disable --now SERVICE
# 2. keep the service, but local only: bind it to 127.0.0.1 in its config, e.g.
# bind-address = 127.0.0.1 (MySQL / MariaDB)
sudo systemctl restart mariadb
# 3. remove the firewall rule that opened it
sudo ufw status numbered
sudo ufw delete 4Run sudo ss -tulpn again afterwards and test from outside once more. That second test is the one that counts.
Frequently asked
Should I use ss or netstat?
Use ss. It is part of iproute2, installed on every current distribution, and sudo ss -tulpn prints the same table as netstat -tulpn: protocol, local address and port, and the process. netstat comes from the old net-tools package, which current Debian, Ubuntu and Rocky images no longer install; it still works if you add it, but nothing requires it.
How do I check if port 22 is open?
On the server, sudo ss -tlnp 'sport = :22' shows whether sshd is listening and on which address. Then test from another machine: nc -zv SERVER_IP 22 on Linux or macOS, or Test-NetConnection SERVER_IP -Port 22 in Windows PowerShell. 'succeeded' or TcpTestSucceeded : True means it is reachable; a timeout usually means a firewall is dropping it.
What is the difference between 0.0.0.0 and 127.0.0.1 in ss output?
0.0.0.0 means the program listens on every IPv4 address of the server, including the public one, so it is reachable from outside unless a firewall blocks it. 127.0.0.1 is the loopback address: only programs on the same server can connect, whatever the firewall says. [::] and [::1] are the IPv6 equivalents.
How do I close an open port on Linux?
Stop the program that listens on it: find the service with systemctl status PID, then run sudo systemctl disable --now on that service. If you need the service but not from outside, set its bind address to 127.0.0.1 and restart it. Remove any firewall rule that opened the port, for example with sudo ufw status numbered and sudo ufw delete N, and test again from another machine.