Download Cheat sheet PDF 12 pages · syntax, editors, patterns, Unicode, performance, debugging
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-255
  • 2[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."


← Back to blog