Networking
IP Addresses, CIDR, and Subnetting Basics You Can't Skip
CIDR, private ranges, and subnetting math without the networking jargon. The foundation every backend engineer should have for firewalls, VPCs, and routing.
Subnetting is one of those topics where the full networking-certification version is overkill for backend engineers, but the pragmatic version is essential. If you configure VPCs, security groups, firewalls, or API rate-limiting by IP, you need it. This guide is the pragmatic version.
IPv4 addresses in bits
An IPv4 address is 32 bits, usually written as four 8-bit numbers separated by dots: 192.168.1.10. Each number is 0-255.
192.168.1.10 in binary:
11000000 . 10101000 . 00000001 . 00001010
That 32-bit number is the whole story. “IP addresses” and “IP networks” are both derived from prefixes of this number.
What CIDR notation means
CIDR (Classless Inter-Domain Routing) writes an IP network as address/prefix_length. The prefix length is the number of leading bits that are the network identifier; the remaining bits are host identifiers within that network.
192.168.1.0/24 means “the network where the first 24 bits are 192.168.1 and the last 8 bits are host identifiers”. That network contains 256 addresses: 192.168.1.0 through 192.168.1.255.
| CIDR | Subnet mask | Total IPs | Usable IPs | Typical use |
|---|---|---|---|---|
| /32 | 255.255.255.255 | 1 | 1 | Single host, firewall rule for one IP |
| /30 | 255.255.255.252 | 4 | 2 | Point-to-point links |
| /29 | 255.255.255.248 | 8 | 6 | Small set of servers |
| /28 | 255.255.255.240 | 16 | 14 | Tiny subnets |
| /27 | 255.255.255.224 | 32 | 30 | Small office |
| /26 | 255.255.255.192 | 64 | 62 | Medium subnet |
| /24 | 255.255.255.0 | 256 | 254 | Classic “Class C”, most LANs |
| /22 | 255.255.252.0 | 1,024 | 1,022 | Building or department |
| /20 | 255.255.240.0 | 4,096 | 4,094 | Large office |
| /16 | 255.255.0.0 | 65,536 | 65,534 | Classic “Class B”, large org |
| /8 | 255.0.0.0 | 16,777,216 | 16,777,214 | ”Class A”, very large |
| /0 | 0.0.0.0 | All IPs | All IPs | ”The internet” |
Usable IPs is total minus 2: the first address in a subnet (host bits all zeros) is the network address, and the last (host bits all ones) is the broadcast address. In most deployments you cannot assign those two to hosts. On point-to-point /31 links RFC 3021 allows using both addresses; modern routers handle this.
The math: a /N network contains 2^(32-N) addresses. /24 is 2^8 = 256. /16 is 2^16 = 65536. Halve the prefix length each step, double the network size.
Private IP ranges
RFC 1918 defines three ranges of IPv4 addresses that are not routable on the public internet. Anyone can use them on their internal network without coordination:
| Range | Size | Typical use |
|---|---|---|
| 10.0.0.0/8 | 16.7M addresses | Large corporate networks, cloud VPCs |
| 172.16.0.0/12 | 1M addresses (172.16.0.0 - 172.31.255.255) | Mid-sized networks, Docker default |
| 192.168.0.0/16 | 65K addresses | Home networks, small offices |
Two others worth knowing:
127.0.0.0/8is loopback.127.0.0.1is “this machine”;::1is its IPv6 equivalent.169.254.0.0/16is link-local (APIPA on Windows, used by cloud instance metadata services:169.254.169.254is AWS/GCP/Azure metadata).
If your application receives a request from a 10.*, 172.16-31.*, or 192.168.* address, the client is on a private network. Internet traffic cannot directly reach those addresses without NAT.
CIDR arithmetic you actually need
Three operations cover 95% of real CIDR work.
Is this IP in this network? Zero out the host bits of the IP and compare to the network address.
import ipaddress
# Is 192.168.1.100 in 192.168.1.0/24?
network = ipaddress.ip_network("192.168.1.0/24")
ip = ipaddress.ip_address("192.168.1.100")
ip in network # True
# Is 10.0.5.5 in any private range?
privates = [
ipaddress.ip_network("10.0.0.0/8"),
ipaddress.ip_network("172.16.0.0/12"),
ipaddress.ip_network("192.168.0.0/16"),
]
any(ipaddress.ip_address("10.0.5.5") in n for n in privates)
# True
How many hosts does this subnet hold? 2^(32-prefix) - 2. Memorize the /24 (254), /16 (65534), /8 (16.7M), and interpolate.
Split a subnet into smaller pieces. A /24 contains two /25s, four /26s, eight /27s, sixteen /28s, and so on — each subnet at level /N gets divided into 2^(M-N) subnets of prefix M. Cloud VPC wizards and subnetting tools handle this for you.
VPC and security group patterns
In AWS, GCP, Azure, or any cloud VPC, the canonical layout is:
- A VPC CIDR like
10.0.0.0/16(65K addresses total). - Subnets inside it like
10.0.1.0/24(254 usable) per availability zone. - Security group rules written in CIDR: “allow port 443 from
0.0.0.0/0” means from the entire internet; “allow port 5432 from10.0.0.0/16” means from inside the VPC only.
Three rules to avoid common mistakes:
- Do not overlap CIDRs. If your VPC is
10.0.0.0/16and you want to peer to another VPC at10.0.0.0/16, they will not talk to each other — the ranges conflict. Pick non-overlapping ranges from the start. - Do not use
/0in a security group rule unless you mean “the whole internet”. A common mistake is “allow port 22 from0.0.0.0/0” on SSH. That is public SSH to the world. You probably want0.0.0.0/0only on port 443 for a public load balancer. - Reserve enough space for growth. A /16 VPC feels like a lot until you spin up EKS with thousands of pods each needing an IP. Most cloud providers recommend starting with /16 or /18 for production VPCs.
Firewalls, rate limiting, and prefix matching
A firewall rule, a rate limit, or a geolocation match against an IP is a CIDR question. Three patterns:
- Individual IP: CIDR
/32. Useful for allow-listing a single admin address. - Small range:
/24or/29for an office or a cloud provider’s edge. - Country-level or provider-level:
/16,/12, or even larger, from a GeoIP database or a provider’s published ranges.
Matching an incoming IP against a list of CIDRs is a standard operation. Libraries implement it with a tree structure (trie) for O(log n) lookup per request. Do not do it with a linear scan against millions of ranges.
IPv6 in one paragraph
IPv6 is 128 bits instead of 32. CIDR works the same way: 2001:db8::/32 is a /32 in IPv6, containing 2^96 addresses. Private addresses live in fc00::/7 (unique local). ::1 is loopback. Most of the mental model transfers; the numbers just get bigger. Modern cloud VPCs increasingly default to dual-stack (IPv4 + IPv6).
Three commands that earn their keep
# List interfaces with their IPs and CIDRs
ip addr show
# Or on macOS:
ifconfig
# Show the routing table (which CIDR goes where)
ip route show
# Or:
netstat -rn
# Reverse DNS for an IP
dig -x 8.8.8.8 +short
# → dns.google.
For the shell reference that pairs with these, see the linux cheatsheet and the bash cheatsheet.
Takeaways
CIDR is address/prefix — the first N bits are network, the rest host. /24 is 256 addresses, /16 is 65K, /8 is 16.7M; halve the prefix, double the network. Private ranges are 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16. Use CIDR in firewalls and security groups deliberately; never write 0.0.0.0/0 on an internal port.