pickuma.
Dev Knowledge

Why Network Ports Go From 0 to 65535: The 16-Bit Story

Port numbers stop at 65535 because TCP and UDP store them in a 16-bit field. Here is what that means, how IANA splits the range, and why low ports need root.

5 min read

You have probably seen :8080, :443, or :3000 hanging off the end of an address and never wondered why the ceiling is 65535 and not, say, 100000. The number is not arbitrary. It falls directly out of how many bits TCP and UDP set aside to store a port.

It is a 16-bit field, so the math is fixed

Both the TCP and UDP headers reserve exactly 16 bits for the source port and 16 bits for the destination port. A 16-bit unsigned integer can represent 2^16 = 65536 distinct values. Counting from zero, that gives you the inclusive range 0 to 65535. There is no extra register, flag, or extension that grows it; the size of the field is the size of the address space.

This is the same reason an 8-bit byte tops out at 255 (2^8 - 1) and a classic unsigned short in C tops out at 65535. A port is, quite literally, an unsigned short. If you ever try to bind to port 70000, the library either rejects it or silently wraps the value, because the number simply does not fit in the slot the protocol gives it.

A port is not a physical thing. It is a label inside a packet that lets one machine run many network services at once. Your laptop has one IP address but can talk to a web server, an SSH daemon, and a database simultaneously because each conversation is tagged with a different port. The operating system uses the combination of (protocol, source IP, source port, destination IP, destination port) to route each packet to the right socket.

How IANA carves up the 65536 values

The numbers themselves are just integers, but the Internet Assigned Numbers Authority (IANA) splits the range into three conventional zones so that software agrees on where common services live:

  • Well-known / system ports — 0 to 1023. These are the famous ones: HTTP on 80, HTTPS on 443, SSH on 22, DNS on 53, SMTP on 25. They are assigned for standard services so that a client can connect to https://example.com without being told the port.
  • Registered ports — 1024 to 49151. Vendors and projects register these with IANA for specific applications, but they are not as tightly controlled. PostgreSQL’s default 5432 and MySQL’s 3306 live here.
  • Dynamic / private / ephemeral ports — 49152 to 65535. Nobody owns these. The OS hands them out temporarily for the client side of a connection.

That last category is the one you rarely type but use constantly. When your browser opens a connection to port 443 on a server, your own machine needs a return address too. It grabs a free ephemeral port for the duration of that connection and releases it afterward. Open 30 browser tabs and your OS is juggling dozens of ephemeral ports behind the scenes.

Why low ports need elevated privileges

On Unix-like systems, binding to any port below 1024 traditionally requires elevated privileges (root, or a specific capability like CAP_NET_BIND_SERVICE on Linux). Ports at or above 1024 can be bound by ordinary users.

The reason is historical and security-minded. The well-known ports map to trusted services, so the rule prevents an unprivileged user from, say, starting a fake SSH server on port 22 and harvesting other people’s passwords. The cost is friction: it is why a Node or Python dev server defaults to something like 3000 or 8080 instead of 80, and why production setups often run the app on a high port and put a privileged reverse proxy in front to listen on 80 and 443.

You can verify which ports are listening with a quick command:

Terminal window
# macOS / Linux
ss -tlnp # or: lsof -i -P -n | grep LISTEN

FAQ

The port number ceiling is one of those facts that looks like a magic constant but is really just a consequence of a design decision made decades ago: give each port a 16-bit slot in the packet header, and 65535 is what you get.

FAQ

Can the same port number be used by both TCP and UDP at once?+
Yes. TCP and UDP maintain entirely separate port spaces. A service can listen on TCP 53 and a different service on UDP 53 with no conflict, because the protocol is part of how the OS identifies a socket. DNS famously uses both.
Why does it start at 0 and not 1?+
Because a 16-bit unsigned integer naturally counts from 0, and the range 0 to 65535 covers all 65536 possible values. Port 0 is reserved rather than usable for a real service, and in practice asking to bind port 0 means 'let the OS assign a free ephemeral port.'
Could ports ever go higher than 65535?+
Not without changing the TCP and UDP packet formats, which would break compatibility with essentially every networked device on the planet. The 16-bit field is baked into the protocols, so the practical answer is no. The address space is plenty large for a single host's needs.

Related reading

See all Dev Knowledge articles →

Get the best tools, weekly

One email every Friday. No spam, unsubscribe anytime.