Common port numbers
The ports you actually run into, with what each is for and why it matters if one is exposed.
| Port | Service | Protocol |
|---|---|---|
| 20 | FTP data | TCP |
| 21 | FTP control | TCP |
| 22 | SSH / SFTP | TCP |
| 23 | Telnet | TCP |
| 25 | SMTP | TCP |
| 53 | DNS | TCP/UDP |
| 67 | DHCP server | UDP |
| 68 | DHCP client | UDP |
| 69 | TFTP | UDP |
| 80 | HTTP | TCP |
| 110 | POP3 | TCP |
| 123 | NTP | UDP |
| 143 | IMAP | TCP |
| 161 | SNMP | UDP |
| 389 | LDAP | TCP |
| 443 | HTTPS | TCP |
| 445 | SMB | TCP |
| 465 | SMTPS | TCP |
| 514 | Syslog | UDP |
| 587 | SMTP submission | TCP |
| 636 | LDAPS | TCP |
| 993 | IMAPS | TCP |
| 995 | POP3S | TCP |
| 1080 | SOCKS proxy | TCP |
| 1433 | Microsoft SQL Server | TCP |
| 1521 | Oracle Database | TCP |
| 1883 | MQTT | TCP |
| 2049 | NFS | TCP/UDP |
| 2375 | Docker API (plain) | TCP |
| 2376 | Docker API (TLS) | TCP |
| 3000 | Node.js / dev server | TCP |
| 3306 | MySQL / MariaDB | TCP |
| 3389 | RDP | TCP |
| 4200 | Angular dev server | TCP |
| 5000 | Flask / dev server | TCP |
| 5173 | Vite | TCP |
| 5432 | PostgreSQL | TCP |
| 5672 | AMQP / RabbitMQ | TCP |
| 6379 | Redis | TCP |
| 8000 | HTTP alternate | TCP |
| 8080 | HTTP alternate | TCP |
| 8443 | HTTPS alternate | TCP |
| 8883 | MQTT over TLS | TCP |
| 9000 | PHP-FPM / SonarQube / Portainer | TCP |
| 9090 | Prometheus | TCP |
| 9200 | Elasticsearch | TCP |
| 11211 | Memcached | TCP/UDP |
| 27017 | MongoDB | TCP |
Know the service but not the number? The same data by name — SMTP, SSH, SQL Server and the rest, including the ones that use more than one port.
The three port ranges
| Range | Name | What it means |
|---|---|---|
0–1023 | Well-known ports | Assigned by IANA to standard services. On Unix, binding to these requires root or the CAP_NET_BIND_SERVICE capability — which is why development servers use 3000 or 8080 instead. |
1024–49151 | Registered ports | Registered with IANA for specific applications, but usable by ordinary user processes. Most database and application servers live here. |
49152–65535 | Dynamic / ephemeral ports | Allocated automatically for the client side of outbound connections. Running out of them is a real failure mode on busy proxies. |
The ports that should never face the internet
If a scan finds any of these open on a public address, treat it as an incident rather than a configuration preference: 23 (Telnet), 445 (SMB), 2375 (Docker API), 3306 (MySQL), 3389 (RDP), 5432 (PostgreSQL), 6379 (Redis), 9200 (Elasticsearch) and 27017 (MongoDB). Each of these has been the root cause of large, well-documented breaches.
Why development servers use 3000 and 8080
On Unix-like systems, binding to a port below 1024 requires root. Rather than run a development server as root, the convention settled on high ports — 3000, 4200, 5173, 8000, 8080 — which any user process can bind. In production a reverse proxy holds 80 and 443 and forwards to the application on its high port.
Binding to a low port without running as root
Running the application as root to get port 80 is the option to avoid: it means a remote code execution in your web server is a remote code execution as root. There are three ordinary alternatives, in roughly the order they are worth reaching for.
- A reverse proxy. nginx, Caddy or a load balancer holds 80 and 443 and forwards to your process on 8080. This is what production almost always does, and it gets you TLS termination and static-file serving as a side effect.
- A capability.
setcap 'cap_net_bind_service=+ep' /path/to/binarylets one specific binary bind low ports without any other root privilege. Note it applies to the binary, so a language runtime given this can bind low ports for every script it runs. - A redirect in the firewall.
iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 8080moves the traffic rather than the permission. It survives restarts only if you persist the rule, which is the usual way this one goes wrong.
macOS has no setcap; use pfctl port forwarding or a proxy. Windows has no privileged range at all — any user can bind port 80 — but it does reserve ranges, and netsh interface ipv4 show excludedportrange protocol=tcp shows which, which is the answer when a port refuses to bind and nothing appears to be using it.
Why a port stays busy after the process is gone
Stop a server and restart it immediately and you can still get "address already in use" with nothing listening. The socket is in TIME_WAIT: TCP holds the pair for twice the maximum segment lifetime — commonly 60 seconds, up to four minutes — so that stray packets from the old connection cannot be delivered to the new one.
The fix is SO_REUSEADDR on the listening socket, which almost every server framework sets for you and which is why you usually never see this. If you are writing the socket code yourself, set it before bind. Do not reach for SO_REUSEPORT instead: it is a different option that lets several processes share one port for load balancing, and it will silently give you two servers where you expected one.
The ephemeral range, and running out of it
Outgoing connections need a source port too, taken from an ephemeral range — 32768–60999 on Linux by default, which is about 28,000 ports. A machine making many short-lived outbound connections can exhaust it, and the symptom is connection failures under load with no listening service anywhere near its limits.
sysctl net.ipv4.ip_local_port_range shows the range and can widen it, but the real fix is usually connection pooling or keep-alive: reusing one connection removes the port from the equation instead of buying more of them. Note the range overlaps ports in the table above — 32768 upward includes nothing standard, which is why the range starts where it does.