One address, many programs
An IP address gets a message to the right machine, but one machine runs many network programs at once: a web server, a database, an SSH login service. A port is a number from 1 to 65535 that picks which program on that machine should receive the message. Address plus port is written 93.184.215.14:443.
Well-known services have standard ports, which is why you rarely type them:
| Port | Service |
|---|---|
| 22 | SSH (remote terminal) |
| 53 | DNS |
| 80 | HTTP (unencrypted web) |
| 443 | HTTPS (encrypted web) |
| 5432 | PostgreSQL database |
When you visit https://example.com, the browser silently connects to port 443. If a URL includes an explicit port, like http://localhost:3000, that overrides the default. Developers see localhost:3000-style URLs constantly: a development server running on your own machine, listening on port 3000.
Ports earn their keep in debugging: a huge share of "cannot connect" bugs are port bugs. The machine is up and the service is running, but the client is dialing port 5432 while the service listens on 5433, and the connection is refused. Whenever a connection fails, "right machine, right port?" is the first question worth asking.
Mapping port numbers to services
A case statement performs the same lookup a scanning tool does when it labels open ports.
for port in 80 443 22 53 8080; do case $port in 22) svc="ssh" ;; 53) svc="dns" ;; 80) svc="http" ;; 443) svc="https" ;; *) svc="unknown" ;; esac echo "port $port: $svc" done
Output
port 80: http port 443: https port 22: ssh port 53: dns port 8080: unknown
Port 8080 falls through to the catch-all, which is honest rather than wrong. 8080 is a common alternative HTTP port by convention, and no rule anywhere makes it so.
That is worth being clear about, because the table of well-known ports is a convention rather than an enforcement. Nothing stops a web server from listening on 5432, and nothing stops a database from listening on 443. The numbers are agreements that keep clients from having to guess.
Which port a browser uses by default
The browser connects to port 443, because https implies it.
Every scheme has a default port. http means 80 and https means 443, so the port is present in the connection even when it is absent from the URL.
There is no such thing as a portless connection. A connection always targets some port, and you only see it written out when it is non-standard, as in localhost:3000.
This is why a development server URL looks different from a production one. Your local app on port 3000 needs the port spelled out, and the same app deployed behind HTTPS does not, which is the same connection made twice with different defaults available.
Splitting host from port
The same trimming trick from lesson 1-2, aimed at a colon.
for addr in db.example.com:5432 cache.example.com:6379 www.example.com:443; do host=${addr%%:*} port=${addr##*:} echo "$host listens on $port" done
Output
db.example.com listens on 5432 cache.example.com listens on 6379 www.example.com listens on 443
Reading the trims
host=${addr%%:*}trims from the first colon to the end, leaving the host.port=${addr##*:}trims everything up to the last colon, leaving the port. The doubled##is deliberate: it takes the longest match, which is what keeps this correct for an IPv6 address full of colons.- Port 6379 is Redis, which is not in the well-known table above and is recognized on sight by anyone who has run a cache. Learning a handful of these numbers is how you read a config file quickly.