TabOnly

CIDR Calculator

Subnet ranges, masks and host counts for IPv4 and IPv6 — including the prefixes most calculators get wrong.

address or block — ipv4 or ipv6, one per lineCtrl+Shift+L
try
cidr10.0.0.0/8
network10.0.0.0
broadcast10.255.255.255
first host10.0.0.1
last host10.255.255.254
netmask255.0.0.0
wildcard0.255.255.255
addresses16,777,216 = 2^24
usable hosts16,777,214
scopeprivate (RFC 1918)
is an address inside this blockpaste one — most calculators do not offer this
split into smaller blocksa longer prefix, up to /32

Everything above is computed here, in this tab. The split list stops at 2,000 blocks; the count beside it is always exact.

The address you pasted may not be the address you get

This calculator refuses 010.0.0.1. That looks unhelpful until you run the same string through the parser that actually decides where a request goes. Measured here in Node v24.12.0, which ships the same WHATWG URL parser as every browser:

You typeThis pageA browser resolves it to
010.0.0.1rejected8.0.0.1 — the leading zero is octal
0x7f.0.0.1rejected127.0.0.1 — hex
2130706433rejected127.0.0.1 — 32-bit integer form
1.1rejected1.0.0.1 — the last part absorbs the gap
192.168.000.001rejected192.168.0.1
1.2.3.4.5rejectedTypeError — rejected there too

A leading zero changes the address, silently, in the only parser whose opinion counts. That is the mechanism behind a long line of allow-list bypasses: something compares the string 010.0.0.1 against a list of blocked internal ranges, finds no match, and the fetch it then makes resolves to 8.0.0.1 — or, with different digits, to a loopback or metadata address. The bug is never in the fetch. It is in the step that treated the text as though it meant what it looked like.

So the parser here is the product, and its rule is reject, never normalise. Every refusal names the address a browser would have resolved the input to and offers both readings as one-click corrections, because you are owed the choice rather than a guess. A calculator built on new URL() gets the normalising for free, and quietly becomes part of the problem; new URL() appears nowhere on this page's parse path, only in its tests, as the oracle that proves the table above.

The three prefixes that break subnet calculators

All three are ordinary inputs, and all three are wrong on a surprising number of pages. Two of them are the same JavaScript defect: the bitwise operators coerce to signed 32-bit, which is just wide enough to look right on IPv4 and be wrong at the edges. Measured in Node v24.12.0:

ExpressionResultWhy it matters
255<<24 | 255<<16 | 255<<8 | 255 -1 255.255.255.255 becomes negative, and every comparison after it is wrong
~0xFFFFFF00 255 the wildcard of a /24 is right by luck; the general case is not
(-1 << 32) >>> 0 0xffffffff shift counts are taken mod 32, so /0 gets the mask 255.255.255.255

0.0.0.0/0 is not a hypothetical prefix. It is the default route and the security-group rule everybody audits, and a calculator that reports its mask as 255.255.255.255 has inverted it. Every value on this page is a BigInt — both families, no Number fast path for IPv4 — which removes that whole class of defect rather than patching each instance of it.

The third is arithmetic that stopped being arithmetic in December 2000:

PrefixAddressesNaive n − 2Correct
/30422
/31202 — RFC 3021, point-to-point
/321−11 — a single host route

A /31 has no network address and no broadcast address, so there is nothing to subtract and both addresses are usable. It is the standard prefix for a router-to-router link, which makes "0 usable hosts" a wrong answer to a very common question. IPv6 removes the subtraction entirely: there is no broadcast address in IPv6 at all, so a /64 has all 18,446,744,073,709,551,616 of its addresses available and the minus-two rule never applies.

IPv6 counts, and why they are exact here

2 ** 64 in JavaScript prints 18446744073709552000. The real count is 18446744073709551616. The difference is not a rounding you can ignore: it is three trailing zeroes on the most common allocation in IPv6, produced by a double that ran out of mantissa. Number.MAX_SAFE_INTEGER is 9,007,199,254,740,991, so every IPv6 prefix of /75 or shorter has a host count outside the range a JavaScript Number can hold — /76 is 252 and exact, /75 is 253 and is not. A ::/0 has 340,282,366,920,938,463,463,374,607,431,768,211,456 addresses, 39 digits, which this page prints in full and also as 2128, because the exponent is the form anyone can actually read.

Text representation gets the same treatment. RFC 5952 is a recommendation with real ambiguity in it, and the rule most hand-written compressors get backwards is the tie-break: when two runs of zero groups are the same length, the leftmost is the one replaced by ::. So 0:0:1:0:0:1:0:0 is ::1:0:0:1:0:0 and not 0:0:1:0:0:1::. One place this page deliberately differs from the platform: for an address inside ::ffff:0:0/96, RFC 5952 §5 recommends keeping the dotted-quad tail, so 0:0:0:0:0:ffff:c0a8:1 is shown as ::ffff:192.168.0.1 where the browser's own parser rewrites it to ::ffff:c0a8:1. Same address, and the page says so; one of the two forms tells you it is 192.168.0.1 in a wrapper.

Zone identifiers — fe80::1%eth0 — are refused rather than stripped. A zone scopes a link-local address to one interface on one machine, so it is not part of the address and no calculation here can use it. new URL() rejects it too.

Where the split list stops, and why that number

Splitting a /8 into /24s is 65,536 blocks. Splitting an IPv6 /32 into /64s is 4,294,967,296 of them. Neither is a list, so the panel above always shows the exact count and materialises the first 2,000 rows. That cap is measured rather than chosen: the split driven end to end in Chrome 148 on the machine this page was built on, median of five runs, from the keystroke to a forced layout.

Blocks renderedMedian
2564.8 ms
1,02411.1 ms
2,04820.3 ms
4,09645.6 ms
8,00096.7 ms

One frame at 60 Hz is 16.7 ms. Two thousand rows is a frame and a bit and reads as instant; eight thousand is a stall you feel on every keystroke in the prefix field, for a list nobody scrolls to the end of. A pasted column has a lower cap — 1,000 lines — for a reason that is visible in the same measurements: a split renders into one text area, while a pasted column renders a row of elements per line, and 2,000 of those cost 95.8 ms against the split's 20.3. Neither limit is about what your machine could compute. The arithmetic is microseconds; drawing is the expensive part, and both numbers are about drawing.

The same answers without this page

shell · the two usual suspects
# Neither ships with macOS; both are a package manager away.
ipcalc 10.0.0.0/8              # network, broadcast, wildcard, host range
ipcalc 192.168.1.0/24 -s 62 62 62   # split into subnets that fit 62 hosts each
sipcalc 2001:db8::/32          # sipcalc is the one that handles IPv6 properly
sipcalc -s 64 2001:db8::/48    # split a /48 into /64s
python · the standard library, and the one to point people at
import ipaddress

n = ipaddress.ip_network('10.0.0.0/8')
n.num_addresses        # 16777216
n.netmask              # 255.0.0.0
n.hostmask             # 0.255.255.255
n.broadcast_address    # 10.255.255.255

ipaddress.ip_network('2001:db8::/64').num_addresses   # 18446744073709551616 — Python has bignums
list(ipaddress.ip_network('10.0.0.0/31').hosts())     # both addresses: RFC 3021 is implemented
[str(s) for s in ipaddress.ip_network('192.168.1.0/24').subnets(new_prefix=26)]
# ['192.168.1.0/26', '192.168.1.64/26', '192.168.1.128/26', '192.168.1.192/26']

ipaddress.ip_address('010.0.0.1')
# ValueError: '010.0.0.1' does not appear to be an IPv4 or IPv6 address
# Python rejects the leading zero too, and has since 3.9.5. It is the right default.
shell · containment, for a script
# Exit status, so it drops straight into an if. Verified with Python 3.9.6.
in_cidr() { python3 -c "import ipaddress,sys; sys.exit(0 if ipaddress.ip_address('$1') in ipaddress.ip_network('$2') else 1)"; }
in_cidr 10.1.2.3 10.0.0.0/8 && echo inside     # inside
in_cidr 11.1.2.3 10.0.0.0/8 || echo outside    # outside

# Filtering a JSON array of addresses, with no ip support in jq (1.7.1):
jq 'def toint: split(".") | map(tonumber) | .[0]*16777216 + .[1]*65536 + .[2]*256 + .[3];
    def inside($net; $bits): toint as $ip | ($net | toint) as $base
      | pow(2; 32 - $bits) as $size | $ip >= $base and $ip < $base + $size;
    map(select(inside("10.0.0.0"; 8)))' ips.json
terraform · where "split a VPC CIDR" actually happens
# cidrsubnet(prefix, newbits, netnum): add newbits to the prefix length,
# then take block number netnum. The same arithmetic as the split panel above.
cidrsubnet("10.0.0.0/16", 8, 0)    # 10.0.0.0/24   — /16 + 8 bits = /24, block 0
cidrsubnet("10.0.0.0/16", 8, 2)    # 10.0.2.0/24   — block 2
cidrsubnet("2001:db8::/48", 16, 1) # 2001:db8:0:1::/64

# The usual shape, one subnet per availability zone:
subnets = [for i, az in local.azs : cidrsubnet(var.vpc_cidr, 8, i)]

What this page will not do

No DNS, no WHOIS, no geolocation, no ASN lookup, and no "what is my IP". Every one of those needs the network, and they are the reason the incumbent pages are cluttered with things you did not come for — this one is instructed by its own response header not to make a request at all, which you can check for yourself. No ping, no traceroute, no port scanning either; those are not a browser's job. And no VLSM planning across a whole address space — allocating a set of differently sized subnets from one supernet against a table of requirements is genuinely useful and genuinely a different tool.

The specs this page implements

  • RFC 4632 — Classless Inter-Domain Routing: the prefix notation itself.
  • RFC 3021 — using 31-bit prefixes on point-to-point links, which is why a /31 shows two usable hosts here.
  • RFC 1918 — private address space (10/8, 172.16/12, 192.168/16), and RFC 6598 — the shared CGNAT space 100.64.0.0/10, which is the range most often mislabelled public.
  • RFC 4291 — IPv6 addressing: the 64-bit interface identifier, multicast in place of broadcast, prefix length in place of a netmask, and the IPv4-mapped form ::ffff:0:0/96.
  • RFC 5952 — the recommended IPv6 text representation: lowercase, no leading zeros in a group, longest run of zeros compressed, leftmost on a tie, and the dotted-quad tail kept for IPv4-mapped addresses.
  • The URL Standard's IPv4 parser — not a spec this page follows, but the one it quotes. It is what turns 010.0.0.1 into 8.0.0.1, and the reason this page refuses that input rather than accepting it.

FAQ

Why does a /31 show 2 usable hosts and not 0?

Because RFC 3021 says so, and because a /31 is the standard way to number a router-to-router link. The usual rule — addresses minus two — exists to remove the network address and the broadcast address, and on a two-address block there is nothing left after removing them. RFC 3021 (December 2000) resolves that by defining a /31 as a point-to-point link with no network and no broadcast address: both addresses are host addresses. The arithmetic a calculator has to get right is therefore not a formula but a policy, and it has three branches: a /30 has 4 addresses and 2 usable, a /31 has 2 addresses and 2 usable, and a /32 has 1 address and 1 usable — not minus one, which is what the formula returns. Python agrees: list(ipaddress.ip_network("10.0.0.0/31").hosts()) returns both addresses, and the /32 returns one. A page that prints "0 usable hosts" for the most common link prefix in a modern network is reporting the formula rather than the network.

Why does a leading zero change the address?

Because the parser that matters — the one in your browser — reads a leading zero as octal. Measured here with Node v24.12.0, which ships the same WHATWG URL parser every browser does: new URL("http://010.0.0.1/").hostname returns 8.0.0.1, not 10.0.0.1. The same rule turns 0x7f.0.0.1 into 127.0.0.1, the bare number 2130706433 into 127.0.0.1, and the two-part form 1.1 into 1.0.0.1. That gap is the mechanism behind a long line of allow-list bypasses: a filter compares the string 010.0.0.1 against a block list of internal ranges, sees no match, and the fetch it then makes goes to 8.0.0.1 — or, with the right digits, straight back to the loopback address. It is why this page refuses all five forms instead of quietly normalising them, and why the refusal names the address you would actually have reached. A calculator built on new URL gets the normalising for free and, in doing so, becomes part of the problem.

Why does IPv6 have no broadcast address and no netmask in dotted form?

Both were removed on purpose. IPv6 has no broadcast at all — RFC 4291 replaces it with multicast, so the traffic that used to go to every host on a link now goes to a multicast group such as ff02::1, and a router can decline to forward it. With no broadcast address there is nothing to subtract, so every address in an IPv6 prefix is usable and the minus-two rule never applies. Carrying it across from IPv4 is the second most common bug in this category after the /31. The dotted netmask went the same way: a 128-bit mask written as eight hex groups would be ffff:ffff:ffff:ffff:: for a /64, which carries no information the number 64 does not, so RFC 4291 defines prefix length as the only notation. The one reservation that does exist is the subnet-router anycast address — the all-zeroes host part, RFC 4291 section 2.6.1 — and it is a reservation for the routers on the link, not a subtraction from your host count.

Why is /64 the standard IPv6 subnet when it holds 18 quintillion addresses?

Because the bottom 64 bits are not an address pool, they are an interface identifier. RFC 4291 fixes the interface ID at 64 bits for every unicast prefix outside 000::/3, and stateless address autoconfiguration (RFC 4862) plus privacy addresses (RFC 8981) both build that half of the address on the host. Make the subnet a /112 to "save space" and SLAAC stops working, which is the actual cost of tidiness here. The count is exactly 18,446,744,073,709,551,616 — and printing it is the reason this page uses BigInt throughout, because 2 ** 64 in JavaScript evaluates to 18446744073709552000, a rounded double whose last three digits are an artefact rather than a number of addresses. Number.MAX_SAFE_INTEGER is 9,007,199,254,740,991, so every IPv6 prefix of /75 or shorter is outside the range a JavaScript Number can count in: /76 is 2^52 and exact, /75 is 2^53 and is not.

What does 0.0.0.0/0 mean in a security group?

Every IPv4 address there is. The prefix length is the number of leading bits that are fixed, so /0 fixes nothing and the block is the whole address space: 4,294,967,296 addresses, mask 0.0.0.0, wildcard 255.255.255.255. In a route table it is the default route — the entry used when nothing more specific matches. In a security group ingress rule it means the open internet, which is right for a public HTTPS listener and is the finding on an SSH or database port. It is also the prefix that breaks calculators, because the obvious way to build a mask in JavaScript is a shift, and shift counts are taken modulo 32: -1 << 32 evaluates to -1, not 0, so a mask built that way reports the default route as 255.255.255.255 — the exact opposite of what it is. Worth knowing that 0.0.0.0/0 does not cover IPv6: the equivalent is ::/0, and a rule that opens one says nothing about the other.

related tools