A listening port can still be blocked. Check it with ss inside and nmap outside, read open, closed and filtered, and open it in ufw.
A service can listen on a port and still be unreachable from the internet. Two different things decide whether a port works: whether a program listens on it, and whether the firewall lets traffic reach it. This guide checks both, from inside the server with ss and from outside with nmap, explains the three results (open, closed, filtered) and opens a port in ufw. Everything was run on a real server.
In short. Inside:
ss -tlnpshows what listens. Outside:nmap -Pn -p PORT serverfrom another machine shows what answers. "Filtered" means the firewall drops the traffic, "closed" means the server answered that nothing listens, "open" means a program answered. Open a port withufw allow PORT/tcp, close it withufw delete allow PORT/tcp.
The test server is a HIP-HOSTING VPS with Ubuntu 24.04.5 and 2 vCPU. Its ufw firewall is on and blocks incoming traffic by default, with rules that allow ports 22, 80 and 443. We scanned it from a second server. In the output the server address is replaced with 203.0.113.20. On a new server ufw may be off; check with ufw status before you rely on any of this. Scan only servers you own.
To have something to test, we start two small web servers: one that listens on every address (0.0.0.0) on port 8000, and one that listens only on the server itself (127.0.0.1) on port 8001. The trailing & keeps them running in the background.
python3 -m http.server 8000 --bind 0.0.0.0 --directory /tmp &python3 -m http.server 8001 --bind 127.0.0.1 --directory /tmp &Now ask the system which programs listen on TCP ports. ss is the modern replacement for netstat. The flags: -t TCP, -l listening sockets only, -n show numbers instead of names, -p show the program (needs root). The part in quotes limits the list to the ports we care about. More about ss itself, including who is connected to your server, is in our article ss instead of netstat.
Read the Local Address:Port column. 0.0.0.0:8000 means "listening on all IPv4 addresses", so the port can be reached from outside if nothing blocks it. 127.0.0.1:8001 means "only from this server itself": an ordinary firewall allow rule will not make that one reachable from the internet. [::]:22 is the same as 0.0.0.0 for IPv6. The last column names the program, which tells you what you would be exposing.
Listening is only half of the story. The firewall decides who may reach the port. Here are the rules for ports 22, 80 and 443 on our server:
Ports 22, 80 and 443 are allowed. Ports 8000 and 8001 are not, and because the default for incoming traffic is "deny", they are blocked even though a program listens on 8000.
The only reliable test is from another machine, not from the server itself. We scanned from a second server. nmap probes ports and reports what it saw; -Pn skips the ping check (many servers do not answer ping) and -p lists the ports. nc -vz -w 3 is a simpler single-port test with a 3-second limit.
Three states to know:
The nc command agrees: after 3 seconds it gives up with "timed out", which is what a dropped connection looks like.
ufw allow 8000/tcp lets TCP traffic to port 8000 in. The comment keyword is optional and helps you remember why a rule exists. We also opened 8001 on purpose, to see what changes for a port that listens only on the server itself:
"Rule added (v6)" means ufw added the same rule for IPv6 too. Port 8000 flipped from filtered to open, and a real request returned HTTP 200. Port 8001 changed from filtered to closed: the firewall now lets the traffic in, but nobody listens on the external address, because the program is bound to 127.0.0.1. curl fails with exit code 7 ("could not connect"). The fix for a port like that is in the program, not the firewall: make it listen on 0.0.0.0, and only if you really want it public.
A rule you no longer need is an open door. List the rules with ufw status numbered, then delete by the same words you used to add:
Both ports are filtered again. We then stopped the test servers. To allow a port only from one address, ufw accepts a form like ufw allow from 203.0.113.50 to any port 8000 proto tcp; we did not test it here.
ufw status first: is there an allow rule for this port and protocol (TCP or UDP)? If the OS firewall is open and the port is still filtered, something in front of the server may drop traffic. We did not test that case; ask support.127.0.0.1. Go back to ss -tlnp.[::] in the ss output; a program may listen on one family only.One warning: never open database ports (3306, 5432, 6379) to the whole internet. Keep them on 127.0.0.1 or allow only the addresses that need them. To get back in if a rule cuts you off, use the web console.
From another machine run nmap -Pn -p PORT server-address or nc -vz -w 3 server-address PORT. "Open" means a program answers. Checking from the server itself shows only whether the program listens, not whether the internet can reach it.
It means the scan got no answer, usually because a firewall drops the traffic. In our test port 8000 was filtered while a program listened on it, until we added an allow rule.
Closed means the server replied that nothing listens on that port: the traffic arrived. Filtered means there was no reply at all, so something blocked it on the way.
Run ufw allow PORT/tcp, then check from outside with nmap. If the port stays closed, the program is not listening on a public address.
No. Install it on a different machine and scan the server from there. nc is enough for a single port.
ss -tlnp inside (who listens) and nmap from another machine (who answers).127.0.0.1 cannot be reached from outside, whatever the firewall says.ufw allow, remove them with ufw delete allow and re-scan after each change.Not tested here: rules limited to one address, IPv6 scans, a firewall in front of the server and systems other than Ubuntu 24.04. Related guides: securing a fresh VPS and connecting to your server.