Ports
Status: 🟩 COMPLETE Last updated: 2026-06-19 Plain-English tagline: A number (0–65535) tagged onto an IP address that identifies WHICH program on that machine the traffic is for. An IP is the building; the port is the apartment number.
In plain English
A computer can run many programs at once that all want to talk to the network: a web server, an SSH server, an email server, a database, a dev server. They all share the same IP address. So how does incoming traffic know which one it’s for?
Ports. Each program “listens” on a specific port number (0-65535). Incoming traffic is addressed to IP:port, and the OS routes it to whichever program is listening on that port.
When you visit https://example.com, you’re actually visiting https://example.com:443 — port 443 is the default for HTTPS, so the browser hides it. Visit http://example.com:80 and you’re using port 80 (default HTTP). Run npm run dev and Next.js typically listens on port 3000 — visit http://localhost:3000.
A port-IP pair (76.76.21.21:443) uniquely identifies a SERVICE on a specific machine. Multiple services on the same machine just use different ports.
For typical webapp work, you encounter:
- 80 — HTTP (default; almost always redirects to HTTPS)
- 443 — HTTPS (default)
- 3000 — Next.js / many Node dev servers
- 5432 — Postgres
- 6379 — Redis
- 22 — SSH
- 3306 — MySQL
This entry covers the protocol concept. For application-level usage (Next.js dev port, Vercel deployment), see What is hosting?.
Why it matters
Three concrete reasons knowing about ports pays off:
-
localhost:3000makes sense. Once you understand ports, “the port is busy” / “address already in use” errors become intelligible instead of mysterious. -
Network debugging. “Why can’t I reach my server?” can be: wrong port, port not open in firewall, another process holding the port, wrong protocol. Each is a different fix.
-
Hosting concepts click. “Edge functions can run on port 80/443 because the platform manages those ports for you” makes sense once you know what ports ARE.
The trade-off: ports are a small concept with a few rules. Reading this entry once is enough; you won’t reference it weekly.
The numbering scheme
Ports are 16-bit unsigned integers — 0 to 65535. They split into three ranges:
| Range | Name | What’s here |
|---|---|---|
| 0–1023 | Well-known (privileged) | HTTP, HTTPS, SSH, FTP, etc. Requires admin/root to bind. |
| 1024–49151 | Registered | Specific apps register here (MySQL 3306, Redis 6379, Postgres 5432). Can be bound by any user. |
| 49152–65535 | Dynamic / ephemeral | Used for short-lived outbound connections. |
The “well-known” privilege requirement is why you can’t run next dev on port 80 without sudo — port 80 needs root. Dev servers default to 3000+ to avoid that.
The well-known ports you’ll meet
| Port | Protocol | What it is |
|---|---|---|
| 21 | FTP | File Transfer Protocol (legacy) |
| 22 | SSH | Secure Shell — remote terminal access |
| 25 | SMTP | Sending email |
| 53 | DNS | Domain Name resolution |
| 80 | HTTP | Web (plain) |
| 110 | POP3 | Reading email (legacy) |
| 143 | IMAP | Reading email |
| 443 | HTTPS | Web (encrypted) |
| 465 | SMTPS | Encrypted SMTP |
| 587 | SMTP | Submission (sending email, modern) |
| 993 | IMAPS | Encrypted IMAP |
For webapp work, only 80 and 443 matter day-to-day. Browsers default to them; almost no website uses anything else publicly.
Application / dev server ports
Common development ports:
| Port | Service |
|---|---|
| 3000 | Next.js (default), Create React App, Express |
| 3001 | Next.js when 3000 is busy (auto-picks) |
| 4200 | Angular CLI |
| 5173 | Vite |
| 5432 | Postgres |
| 6379 | Redis |
| 8000 | Django (default) |
| 8080 | Tomcat, common alternate HTTP |
| 8443 | Common alternate HTTPS |
| 9000 | Various tools |
| 27017 | MongoDB |
For Bible Quest-style projects, you’ll see 3000 (Next.js) and 5432 (if you ever connect directly to Supabase Postgres) most often.
A concrete example: visiting a site
When you type https://example.com/about in the browser:
- Browser parses URL → host
example.com, path/about, port (default for HTTPS) = 443 - DNS resolves
example.com→76.76.21.21 - Browser opens TCP connection to
76.76.21.21:443 - TLS handshake on port 443
- HTTP request sent over the encrypted connection
- Response received
- Page renders
For http://localhost:3000/:
- Browser parses URL → host
localhost, path/, port3000 - DNS resolves
localhost→127.0.0.1 - Browser opens TCP connection to
127.0.0.1:3000 - No TLS for plain HTTP
- HTTP request sent
- Whatever’s listening on 3000 (your
next devserver) responds
The mechanism is identical; the destination port differs.
Listening vs connecting
A program can have two roles relative to ports:
- Listening on a port — “I’m ready to accept incoming connections here.” Web servers, dev servers, databases all listen.
- Connecting to a port — “I want to talk to whoever’s listening at that address.” Browsers, API clients, anything that initiates a request.
A typical Next.js dev server:
Listening on port 3000.
Browser connects: opens an ephemeral local port (e.g. 51234) → talks to 127.0.0.1:3000.
Each TCP connection has FOUR identifiers: (source IP, source port, destination IP, destination port). The ephemeral source port is automatic — the OS assigns one from the 49152-65535 range. You almost never set it manually.
What “Address already in use” means
When you start a server on port 3000 and get:
Error: listen EADDRINUSE: address already in use :::3000
Another process is already listening on port 3000. Maybe a leftover next dev from earlier. To fix:
Windows:
# Find what's on port 3000
Get-NetTCPConnection -LocalPort 3000
# Or older style
netstat -ano | findstr :3000
# Kill the process by PID
Stop-Process -Id <PID> -ForceMac/Linux:
# Find what's on port 3000
lsof -i :3000
# Or
sudo netstat -tulpn | grep :3000
# Kill the process by PID
kill -9 <PID>Or simply start your dev server on a different port:
# Next.js
PORT=3001 npm run dev
# Or
next dev -p 3001In modern Next.js, if 3000 is busy, it automatically tries 3001, then 3002, etc.
Ports in hosting
In production webapp hosting, ports are mostly INVISIBLE to developers:
- Vercel, Netlify, Cloudflare Pages — your function code doesn’t bind to a port. The platform handles HTTP/HTTPS routing on 80/443 for you. You just export handlers.
- Heroku, Render, Fly.io — your app reads the
$PORTenv var and listens on it. The platform routes external 80/443 traffic to your$PORT. - AWS EC2 / DigitalOcean Droplets / your VPS — you bind to whichever ports you want, configure firewalls to allow them, set up SSL termination yourself.
For Bible Quest (Vercel), the hosting layer means “port” is mostly an internal-dev concept. You don’t configure ports for production.
Common gotchas
-
localhost:80doesn’t usually need a port in the URL.http://localhost/works because 80 is the default.http://localhost:80/is the same. -
Browsers REJECT non-standard ports for HTTPS by default.
https://example.com:8443/works but feels weird. Some browsers warn for unusual ports on HTTPS. -
Some networks block non-standard ports. Corporate or school networks may only allow 80 and 443 outbound. Your dev server on port 3000 may be unreachable from a coffee shop.
-
PORTenv var is the modern convention. Heroku started it; now Render, Fly, Railway all use it.process.env.PORT || 3000is the idiomatic fallback. -
Privileged ports (under 1024) require admin on most OSes. A Node app binding to port 80 needs
sudoorsetcap. Vercel/AWS/etc. handle this by running your code as a non-privileged user behind a load balancer that owns port 80. -
Dev tools commonly fight over 3000. Many local databases, tools, and dev servers want port 3000. Use 3001+ if you have a habitual port conflict.
-
Multiple processes can’t bind the same port. EADDRINUSE means another process owns it. The OS won’t share.
-
0.0.0.0and127.0.0.1differ for binding.0.0.0.0:3000accepts connections from ANYWHERE on the network.127.0.0.1:3000only from this machine. Dev servers usually bind 127.0.0.1 for safety; production servers bind 0.0.0.0. -
Firewalls can silently drop port traffic. A server listening on port 3000 + a firewall blocking 3000 = “can’t reach the server” with no error from the server’s perspective. Test from both directions.
-
Port forwarding on routers (NAT) — to expose a home server publicly, the router needs to forward an external port to your machine’s internal port. Otherwise the public can only reach the router, not the device behind it.
-
Tunneling services (ngrok, Cloudflare Tunnel) create a public URL that maps back to a local port. Saves dealing with NAT/firewalls during development. See Windows dev environment.
-
Some platforms restrict outbound ports. Vercel Functions can outbound to most ports, but some cloud providers restrict outbound 25 (SMTP) to prevent spam from compromised servers.
-
Wireshark / browser DevTools don’t show ports often. They focus on URLs. To see port-level traffic, use
tcpdump,nslookup, ornetstat. -
http://localhost:3000/apiis a connection to port 3000. The/apiis just the URL path; the port determines which process answers. -
WSL2 and Windows host share ports asymmetrically. A server inside WSL bound to
0.0.0.0:3000is reachable from Windows host aslocalhost:3000. The reverse isn’t always true; WSL networking has its quirks. -
Docker maps host ports to container ports.
docker run -p 3000:3000means “host port 3000 → container port 3000.” You can map differently:-p 3001:3000makes the container’s 3000 reachable from host’s 3001. -
docker-compose.ymlusesports: ["3000:3000"]for the same pattern. First number is host, second is container. -
process.env.PORTis a STRING, not a number.parseInt(process.env.PORT)or use loosely (http.listen(process.env.PORT)accepts string). -
The OS may refuse to release a port after a process crashes. TIME_WAIT state can hold a port for 30-60 seconds. Annoying when iterating during dev.
kill -9skips graceful shutdown but doesn’t immediately free the port; just wait. -
Some Bonjour/mDNS services use port 5353. This is why “find devices on the network” works without manual IP entry — they announce on 5353.
-
Port scanning is detectable. Repeatedly trying random ports against a server is treated as reconnaissance and gets blocked / logged by firewalls. Don’t do it without permission.
-
A “service” running on a port can be ANYTHING. Port 80 doesn’t mean HTTP — it means “whatever’s listening here.” A misconfigured server could put a database on port 80. Always verify what’s actually listening.
-
netstat -anshows you all listening ports + connections on most OSes.ss -tulpnis the modern Linux equivalent. Useful for debugging “what’s running on this machine?” -
telnet host portchecks if a port is reachable. Open a TCP connection tohost:port— if it connects, the port is open. Modern alternative:nc -zv host port. Quick test. -
HTTPS on a non-443 port still works. Browsers do TLS to whatever port you ask. But certificate validation works the same way — the certificate must cover the hostname.
-
TLS termination usually happens at a load balancer. Behind a Vercel function or a Cloudflare worker, the request comes in on port 443 (HTTPS) and is passed to your function in plain HTTP — TLS is “terminated” at the edge. Your function never sees the encrypted bytes.
-
Reserved ports for testing: 1024-49151 ephemeral range avoids well-known. For local testing of custom services, pick something in 8000-9000 or 30000-40000 to avoid conflict.
-
PostgreSQL default is 5432, but Supabase pgbouncer is 6543. Direct connection vs pooled connection. Different ports for different purposes on the same logical service.
A practical reference
For Bible Quest specifically, the ports you’ll see:
- Local dev:
localhost:3000(Next.js) - Local Supabase (if running locally):
localhost:54321(REST API),localhost:54322(Postgres direct) - Production: Vercel handles 443 / HTTPS transparently. Supabase Postgres on 5432/6543 (you don’t expose this publicly)
Most of the time you just think localhost:3000. The rest is platform internals.
See also
- IP addresses 🟩 — addresses on top of which ports operate
- HTTP & HTTPS 🟩 — ports 80, 443
- TCP vs UDP 🟩 — both protocols use ports
- DNS — deep dive 🟩 — UDP port 53
- What is hosting? đźź©
- How the web works đźź©
- Windows dev environment 🟩 — port forwarding in WSL
- Environment variables 🟩 —
process.env.PORT - Node.js (runtime) 🟩 🟦
- Postgres 🟩 🟦 — port 5432
- Supabase 🟩 🟦
- Glossary: Port, Socket, Localhost
Sources
- IANA Service Name and Transport Protocol Port Number Registry — official registry
- MDN — Identifying resources on the Web — ports as part of URLs
- Cloudflare — What is a computer port?
- Wikipedia — List of TCP and UDP port numbers — comprehensive
- RFC 6335 — port number allocation