Regex for IPv4 Address Validation — Getting the Octet Range Right

IPv4 validation looks trivial until you realize the naive "four groups of digits separated by dots" pattern accepts garbage like 999.999.999.999. This guide walks through the common mistake, the correct fix, and a few real-world variations you will need for CIDR ranges and private IP detection.

The Common Mistake

An IPv4 address is four "octets" (0-255) separated by dots. The tempting shortcut is to match 1-3 digits per octet — but digits alone don't enforce the 0-255 ceiling:

// WRONG — accepts invalid octets above 255
const naiveIp = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;

naiveIp.test('192.168.1.1');       // true  (correct)
naiveIp.test('999.999.999.999');   // true  (WRONG — should be false!)
naiveIp.test('300.1.1.1');         // true  (WRONG — 300 is not a valid octet)

The Correct Pattern

Each octet needs an explicit alternation that covers three numeric ranges: 250-255, 200-249, and 0-199 (with optional leading zero forms):

const octet = '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)';
const ipv4Regex = new RegExp('^' + octet + '\\.' + octet + '\\.' + octet + '\\.' + octet + '$');

// Equivalent inline form:
const ipv4 = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;

ipv4.test('192.168.1.1');      // true
ipv4.test('255.255.255.255');  // true
ipv4.test('999.999.999.999');  // false — correctly rejected
ipv4.test('300.1.1.1');        // false — correctly rejected
ipv4.test('0.0.0.0');          // true

Breaking down the octet group (25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?):

  • 25[0-5] — matches 250 through 255
  • 2[0-4][0-9] — matches 200 through 249
  • [01]?[0-9][0-9]? — matches 0 through 199 (with 1-3 digits, optional leading 0/1 hundreds digit)

Reusable Named-Group Version

If you need to extract each octet individually (for logging, sorting, or CIDR math), use named capture groups:

const ipv4Named = /^(?<a>25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?<b>25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?<c>25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?<d>25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;

const match = '192.168.1.1'.match(ipv4Named);
console.log(match.groups); // { a: '192', b: '168', c: '1', d: '1' }

Detecting Private / Reserved IP Ranges

A common follow-up need is checking whether a validated IP falls in a private range (useful for firewall rules, log filtering, or access control):

  • 10.0.0.0 – 10.255.255.255/^10\./
  • 172.16.0.0 – 172.31.255.255/^172\.(1[6-9]|2[0-9]|3[01])\./
  • 192.168.0.0 – 192.168.255.255/^192\.168\./
  • 127.0.0.0 – 127.255.255.255 (loopback)/^127\./

Frequently Asked Questions

Why does \d{1,3} for each octet not work for IPv4 validation?

Because \d{1,3} matches any one-to-three digit number, including values above 255 like 999 or 256, which are not valid IPv4 octets (0-255). It needs to be replaced with an explicit numeric range pattern.

What is the correct regex for validating an IPv4 address?

A correct pattern is ^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$, which restricts each of the four octets to the valid 0-255 range.

Does this regex also validate IPv6 addresses?

No. IPv6 uses a completely different address format with hexadecimal groups separated by colons, such as 2001:0db8:85a3::8a2e:0370:7334, and needs its own dedicated regex or, better, the built-in URL/net parsing utilities in your language.

Try the Free AI Regex Generator

Describe any validation rule in plain English and get a working regex instantly — no signup required.

Related articles