Why people keep saying "expose as little beyond 443"
When setting up a server, the most common security advice is "only open 80/443 if you can". The reason is simple: the more ports you open, the larger your attack surface—every open port is a potential entry point. To put that advice into action you first need to understand ports themselves.
What a port is, and why 65535 of them
A machine runs web, database, SSH and more at once. How does the OS hand each network packet to the right app? Via port numbers: the destination port in a TCP/UDP header decides which app handles the connection.
TCP header: [source port][destination port][sequence]…
destination port = which app on this machine handles this connection
0–65535 is 65536 numbers: 1–1023 are well-known ports (often needing privileged binding), 1024–49151 are registered ports, and 49152–65535 are dynamic/private.
Common ports by purpose
Web and encryption
| Port | Service | Notes |
|---|---|---|
| 80 | HTTP | often 301 → 443 |
| 443 | HTTPS | encrypted, a must-open |
| 8443 | HTTPS alt | web admin / backup |
Remote admin & ops
| Port | Service | Notes |
|---|---|---|
| 22 | SSH | suggest changing default / keys / source IP |
| 3389 | RDP (Windows) | strongly discourage public exposure |
| 21 | FTP | plaintext; prefer SFTP/SCP |
File transfer
| Port | Service | Notes |
|---|---|---|
| 21 | FTP | plaintext |
| 22 | SFTP/SCP | over SSH, recommended |
| 990 | FTPS (implicit) | encrypted FTP |
Databases
| Port | Service | Notes |
|---|---|---|
| 3306 | MySQL/MariaDB | usually internal only |
| 5432 | PostgreSQL | internal only |
| 6379 | Redis | never expose publicly |
| 27017 | MongoDB | never expose publicly |
Dev & debug
| Port | Service | Notes |
|---|---|---|
| 3000 | Node/Vite dev | local binding is fine |
| 3300 | common dev service | local use |
| 5000/8000 | Flask / generic | bind to localhost |
| 53 | DNS | UDP, service-private |
Mail and more
| Port | Service | Notes |
|---|---|---|
| 25 / 587 | SMTP / submission | 25 often blocked by clouds |
| 110 / 143 | POP3 / IMAP | plaintext; prefer encrypted |
| 993 / 995 | IMAPS / POP3S | encrypted, recommended |
| 443/993 | web / encrypted mail |
Source vs destination port (in one line)
- Destination port = the service's "doorplate" the client connects to;
- Source port = a random high port the client picks when initiating, so the server can reply to the right flow.
Outbound source ports are random and rarely need to be opened; you only care about inbound "which destination port to expose".
How services are discovered (port scanning)
The port-service mapping isn't magic: a service answers on the port it listens on, so probing whether a given IP has a live service on a port can be checked. That's one technique shared by both self-audits and attackers' reconnaissance—the difference is you scan yourself, they scan you. So minimal exposure + only necessary inbound ports is the most direct way to shrink your scannable surface.
Security baseline checklist
- [ ] Inbound only: 80/443 + the app ports you need,
denyeverything else; - [ ] SSH: non-default port / key-based login / source-IP limits (at least one);
- [ ] Databases, Redis, Docker and debug ports: bind to internal/localhost only, never public;
- [ ] Enforce rules with cloud security groups/host firewalls rather than "open everything and fix later";
- [ ] Periodically scan your own public IP and confirm no admin management port is accidentally open.
Self-check
On a server you manage, use an online port/IP lookup to list what it currently exposes, then trim against the baseline above and ask yourself once: "does this port really need to be reachable from the public internet?" More often than not the answer is no—removing one port removes one attack surface.
Reproducible Results
Do not memorise the port table — ask the OS. Python's standard library ships the IANA services database:
import socket
socket.getservbyname("ssh", "tcp") # → '22'
socket.getservbyname("http", "tcp") # → '80'
socket.getservbyname("https", "tcp") # → '443'
The reverse lookup is socket.getservbyport(22). For the full cross-reference (including UDP and common middleware ports) use the common ports reference and the random port generator, which avoids the privileged 0–1023 range.