If you've run nmap against your server from the outside and the output shows anything more than 22, 80, and 443, you have a decision to make right then: which of these ports should be closed and which one really needs to stay open. This text is that reference; not an installation tutorial, not an introduction.
One note before the table: a "service port" is not just a number. It's the combination of protocol, port, and bind address that gives it meaning. 3306/tcp on 127.0.0.1 is one thing, and the same port on 0.0.0.0 is another. Most of the incidents I've seen have been of the second kind, not the first.
Table of Common Service Ports
| Port | Protocol | Service | Open on the internet? |
|---|---|---|---|
| 22 | TCP | SSH | Only with key and IP restriction |
| 25 | TCP | SMTP | Almost never (most providers block it) |
| 53 | TCP/UDP | DNS | Only public resolver; not open recursive |
| 80 | TCP | HTTP | Yes, only for redirect to HTTPS |
| 110 / 143 | TCP | POP3 / IMAP | Only TLS version |
| 443 | TCP/UDP | HTTPS / HTTP3 | Yes |
| 587 / 465 | TCP | SMTP submission | Yes, with authentication |
| 993 / 995 | TCP | IMAPS / POP3S | Yes |
| 1433 | TCP | MSSQL | No |
| 3000 / 8000 / 8080 | TCP | Application (Node, Django, …) | No; behind reverse proxy |
| 3306 | TCP | MySQL / MariaDB | No |
| 5432 | TCP | PostgreSQL | No |
| 6379 | TCP | Redis | No, never |
| 27017 | TCP | MongoDB | No |
Take the last column seriously. Ports 6379, 27017, and 3306 are the biggest victims in automated scans, because the default installation of many distributions binds them on all interfaces and the user doesn't notice.
Ports Whose Being Open Means Disaster
I'll separate three categories, because their solutions aren't the same.
1. Databases
MySQL, PostgreSQL, Redis, and MongoDB should never be accessible from the internet. If the application is on the same server, move the bind to local. In my.cnf:
[mysqld]
bind-address = 127.0.0.1
skip-networking = 0
In PostgreSQL too, set listen_addresses = 'localhost' in postgresql.conf and allow only 127.0.0.1/32 in pg_hba.conf. If the database is on another server, use an SSH tunnel, not opening the port. The guide MySQL security and restricting access covers this path step by step.
2. Redis and Services Without Authentication
Redis has no password by default. A fresh installation on a cloud server gets scanned within minutes to hours. If the output of redis-cli -h <ip> ping from the outside returns PONG, your server is in danger right now. The solution: bind 127.0.0.1 ::1 along with protected-mode yes and, if needed, requirepass.
3. Management Panels and Debug Ports
Port 8080 for a panel, 9200 for Elasticsearch, 2375 for Docker daemon without TLS. The last one is the worst: access to 2375/tcp means running an arbitrary container on your server with root access. If you've just set up Docker, read setting up Docker on a VPS and expose the socket only via TLS or SSH.
How to Find Out What's Really Open
From inside the server, ss -tulpn gives the list of listeners. But this isn't enough; because the firewall may be blocking some of them. The real test is from the outside:
nmap -sS -Pn -p- --min-rate 2000 203.0.113.10
The -p- flag scans all 65535 ports and --min-rate increases the speed. A full scan on a normal server takes between 30 and 90 seconds. If you don't want to scan from the outside, at least run ss -tulpn | grep -v 127.0.0.1 to see what's listening on 0.0.0.0.
For the firewall, on newer systems nftables and on older ones ufw or firewalld are common. A simple nftables rule that only leaves 22, 80, and 443 open:
nft add rule inet filter input tcp dport { 22, 80, 443 } ct state new accept
nft add rule inet filter input ct state established,related accept
nft add rule inet filter input drop
Order matters. If you put the drop rule first, you'll cut yourself off too. Always keep a second SSH session open before applying.
This Is Where They Make Mistakes
The most common mistake I've seen is this: the user closes the port in the firewall, feels relieved, but the service is still listening on 0.0.0.0. Then one day they temporarily turn off the firewall for something else, and at that very moment the database is exposed. The sign is also clear: in the MySQL log, lines like Aborted connection ... (Got an error reading communication packets) from unknown IPs, or in Redis a sudden jump in keyspace_hits without application traffic.
Second mistake: leaving port 22 open for everyone and relying on a password. Brute-force attacks on SSH on public servers have thousands of attempts daily. Set PasswordAuthentication no and PermitRootLogin prohibit-password in sshd_config and use a key instead of a password.
Non-Standard Port: Is It Worth It?
Moving SSH to an unusual port like 2222 or 49152 drastically reduces the volume of automated scans. But it doesn't bring real security; it only reduces noise. I personally do this, but as a second layer, not a replacement for keys and firewall. If you have a support team or monitoring tool that depends on port 22, the cost is more than the benefit, and the same 22 with IP restriction is enough.
On the other hand, for internal services like an API between two servers, a non-standard port on the private network is worth it. If the servers are on an internal network, don't use a public port at all.
When You've Lost Access
The worst case is applying the firewall rule incorrectly and SSH getting cut off. If you have console access, log in via KVM or the web console and revert the rule. If you don't, you have to take the server to rescue mode and mount the filesystem from the outside. The steps are in reinstalling the server and working with rescue mode. A personal rule: before any firewall change, take nft list ruleset > /root/nft-backup-$(date +%F).txt. It takes thirty seconds and eliminates a sleepless night.
If your server is on cloud infrastructure, there's usually a Security Group layer on top of the OS firewall as well. These two add up, not replace each other. Close the port in the OS and don't close it in the Security Group, and vice versa. If you're working on a cloud server, factor this into the initial design so you don't have to rewrite all the rules later.
Frequently Asked Questions
What is port 8080 for and should it be open?
Port 8080 is usually used as an alternative HTTP port; for management panels, Tomcat, Jenkins, and reverse proxies. Having it open on the internet is almost always wrong. If the application is listening on 8080, put it behind Nginx on 443 and leave 8080 itself open only on 127.0.0.1.
How do I find out which ports are open on my server?
From inside the server, see the list of listeners with ss -tulpn, and confirm from the outside with nmap -sS -Pn -p- <ip>. The difference between these two outputs is what the firewall has blocked. Don't trust only the inside output, because the firewall may be turned off later.
Does changing the SSH port increase security?
A little, but not as much as is imagined. The main benefit is reducing log noise and automated scans. Real security comes from disabling password login, using a key, and IP restriction. If you have these three, changing the port is optional.
I closed port 3306 but MySQL still responds from the outside; why?
Because the firewall rule is probably applied in the wrong chain or in the wrong order, or the service is bound to another interface. Check with ss -tulpn | grep 3306 what the bind address is. If it's 0.0.0.0:3306, the firewall is your only layer of defense and you should also fix bind-address.
Right now, run ss -tulpn once and note down every line that starts with 0.0.0.0 or ::. Then for each one, write a sentence about why it should be accessible from the internet. The line you don't have an answer for is the port you should close tonight.