Cheatsheet

TCP and UDP Ports Cheatsheet

This cheatsheet lists the port numbers developers and sysadmins look up most often, grouped by job, plus the port ranges and a few commands for checking a port. A port is a 16-bit number, so the valid range is 0 to 65535, and TCP and UDP each have their own separate set of 65,536. The confusion it clears up: some numbers below are IANA assignments, and some are only common defaults that the registry does not list for that product.

Quick reference

The three port ranges

Range Name Notes
0-1023 System Ports (well-known) Assigned by IANA. Unix systems usually need elevated rights to listen here
1024-49151 User Ports (registered) Assigned by IANA on request
49152-65535 Dynamic Ports (private, ephemeral) Never assigned. Meant for the client side of a connection

Linux does not use the IANA dynamic range as is. On the machine used for this sheet, /proc/sys/net/ipv4/ip_local_port_range held 32768 60999.

Web and remote access

Port Typical transport Service Notes
22 TCP SSH Also registered for UDP, but SSH uses TCP
23 TCP Telnet Plain text, avoid
80 TCP HTTP Registered names: http, www, www-http
443 TCP, UDP HTTPS IANA lists both transports
8080 TCP HTTP alternate Registered as http-alt
8443 TCP Alternate HTTPS IANA name is pcsync-https; used by convention for HTTPS
3389 TCP, UDP Remote Desktop (ms-wbt-server)
5900 TCP VNC (rfb)

Email

Port Service Notes
25 SMTP Server to server relay
587 Submission Mail clients sending through their provider
465 Submissions SMTP over TLS from the start
110 POP3 Plain
995 POP3S POP3 over TLS
143 IMAP Plain. Only TCP is assigned; UDP is reserved
993 IMAPS IMAP over TLS

Name, time and file services

Port Typical transport Service
53 TCP, UDP DNS
853 TCP, UDP DNS over TLS (TCP), DTLS or QUIC (UDP)
123 UDP, TCP NTP
67, 68 UDP DHCP server and client (registered as bootps and bootpc)
69 UDP TFTP
21 TCP FTP control
20 TCP FTP data
445 TCP SMB (microsoft-ds)
2049 TCP, UDP NFS
873 TCP rsync daemon
389 TCP, UDP LDAP
636 TCP LDAPS
161, 162 UDP SNMP queries and traps
514 UDP Syslog

Databases and brokers

Port Service Registered by IANA?
3306 MySQL Yes
5432 PostgreSQL Yes
1433 Microsoft SQL Server Yes
6379 Redis Yes, TCP only
27017 MongoDB Yes, TCP only
11211 Memcached Yes
5672 RabbitMQ and AMQP Yes (amqp)
5671 AMQP over TLS Yes (amqps)
1883, 8883 MQTT, MQTT over TLS Yes
5984 CouchDB Yes
1521 Oracle Database default No. IANA lists ncube-lm
9200 Elasticsearch HTTP default No. IANA lists wap-wsp

Containers and infrastructure

Port Service Registered by IANA?
2375 Docker API, unencrypted Yes (docker)
2376 Docker API over TLS Yes (docker-s)
6443 Kubernetes API server default No. IANA lists sun-sr-https
51820 WireGuard default No entry found in the registry
1194 OpenVPN Yes
500, 4500 IPsec IKE and NAT traversal Yes, UDP
179 BGP Yes
3478 STUN and TURN Yes
5060 SIP Yes

Header facts worth knowing

Fact Value Source
Port field size 16 bits, so 0 to 65535 TCP and UDP headers
Smallest UDP length field 8 octets, the header alone RFC 768
TCP header length Counted in 32-bit words in the Data Offset field RFC 9293
Source port in UDP Optional. A zero means none RFC 768

A connection is identified by four values: source address, source port, destination address and destination port. That is why a server on port 443 can talk to thousands of clients at once, since each client brings a different source port.

Common patterns

Look up a port name from the system services database

import socket
for n in ['http', 'https', 'ssh', 'smtp', 'domain', 'postgresql', 'mysql']:
    print(n, socket.getservbyname(n, 'tcp'))
print(socket.getservbyport(53, 'udp'))

Output:

http 80
https 443
ssh 22
smtp 25
domain 53
postgresql 5432
mysql 3306
domain

Use this when a script has a service name and needs the number. It reads the local services file, so the answer depends on the machine.

Check whether a port is listening

ss -ltn 'sport = :18080'

Output with a test listener running:

State  Recv-Q Send-Q Local Address:Port  Peer Address:PortProcess
LISTEN 0      128        127.0.0.1:18080      0.0.0.0:*

Use -l for listening sockets, -t for TCP and -u for UDP. Add -p to see the owning process, which may need root.

Test whether a remote port accepts connections

nc -zv 127.0.0.1 18080
nc -zv 127.0.0.1 18081

Output:

Connection to 127.0.0.1 18080 port [tcp/*] succeeded!
nc: connect to 127.0.0.1 port 18081 (tcp) failed: Connection refused

The flag -z scans without sending data. A refused connection means the host answered and nothing listens. A timeout usually means a firewall dropped the packet.

Ask the OS for a free port

import socket
s = socket.socket()
s.bind(('127.0.0.1', 0))
print(s.getsockname()[1] >= 1024)

Output:

True

Binding to port 0 makes the kernel choose an unused port. Read the real number back with getsockname(), which is how test suites avoid port clashes.

See the error when a port is taken

import socket
a = socket.socket(); a.bind(('127.0.0.1', 0)); a.listen()
p = a.getsockname()[1]
b = socket.socket()
try:
    b.bind(('127.0.0.1', p))
except OSError as e:
    print(e)
u = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
u.bind(('127.0.0.1', p))
print('udp same number bind ok')

Output:

[Errno 98] Address already in use    (Linux; Windows reports WinError 10048)
udp same number bind ok

Two TCP listeners cannot share an address and port, but a UDP socket can use the same number as a TCP one.

See which port a URL really uses

for (const u of ['http://example.com:80/', 'https://example.com:443/', 'https://example.com:8443/', 'http://example.com:443/']) {
  const x = new URL(u);
  console.log(u, '=> port:', JSON.stringify(x.port), 'host:', x.host);
}

Output:

http://example.com:80/ => port: "" host: example.com
https://example.com:443/ => port: "" host: example.com
https://example.com:8443/ => port: "8443" host: example.com:8443
http://example.com:443/ => port: "443" host: example.com:443

The URL class drops a port that matches the scheme default and returns an empty string. Code that reads url.port to build a socket address must fall back to 80 or 443 itself.

Pitfalls

  • Assuming a default port is an official one: 1521 for Oracle, 9200 for Elasticsearch and 6443 for Kubernetes are vendor defaults. The IANA registry assigns those numbers to other names, so firewall tools that map numbers to names show the wrong label.
  • Opening a port for one transport only: 53 needs both TCP and UDP, and 443 is listed for both. A firewall rule for TCP alone leaves UDP traffic blocked.
  • Using port 65536 or negative numbers: a port is 16 bits. Python raises OverflowError: bind(): port must be 0-65535. for 65536.
  • Treating 0 as a real port: binding to 0 means "pick one for me". The port is not usable as a fixed address.
  • Expecting a number to be in use only once across protocols: TCP 8080 and UDP 8080 are independent endpoints. The Address already in use error appears only within the same transport and address.
  • Mixing up 25, 465 and 587: port 25 is for server-to-server relay, 587 is for client submission, and 465 is the implicit-TLS submission port. A mail client pointed at 25 is often blocked by the provider.
  • Reading a closed port as a firewall block: Connection refused comes from the target host, which means the packet arrived. A silent timeout points to a filter in between.
  • Reading url.port as always set: new URL('https://example.com:443/').port is an empty string, and a port above 65535 throws ERR_INVALID_URL. Use a default-port lookup before connecting.
  • Exposing 2375: the Docker API on 2375 is unencrypted. Use 2376 with TLS, or keep the daemon on its local socket.

Related ZipKit tools

Related cheatsheets