Blog
Strict IPv4 address regex (with octet range validation)
A naive <code>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}</code> regex matches 999.999.999.999, which obviously isn't a real IP. Here's the strict version.
The strict pattern
const IPV4_RE = /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/;
IPV4_RE.test("192.168.1.1"); // true
IPV4_RE.test("255.255.255.255"); // true
IPV4_RE.test("256.1.1.1"); // false
IPV4_RE.test("1.2.3"); // false
The inner group (25[0-5]|2[0-4]\d|[01]?\d\d?) enforces 0-255 per octet:
25[0-5]— 250-2552[0-4]\d— 200-249[01]?\d\d?— 0-199 (handles 1, 12, 123 with optional leading 0 or 1)
The lenient pattern
/^\d{1,3}(\.\d{1,3}){3}$/
Use this when you trust the source (logs from a real server, output of an IP-issuing API). It's shorter, faster, and easier to read.
For non-anchored extraction
Drop the anchors when extracting IPs from logs:
const IPV4_FIND = /\b((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)\b/g;
const log = "Connection from 192.168.1.1 to 10.0.0.5 refused";
const ips = log.match(IPV4_FIND);
// ["192.168.1.1", "10.0.0.5"]
Special-purpose ranges
Some use cases need ranges:
- Private IPs: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16. Use range checks in code after regex, not a regex.
- Loopback: 127.0.0.0/8.
^127\.as a prefix check. - Link-local: 169.254.0.0/16.
^169\.254\.prefix.
For CIDR membership, parse the IP and the network, then compare numerically. Regex tells you "is this an IP"; arithmetic tells you "is it in this subnet."