Regex for IPv4 addresses with real octet range checks
A working IPv4 pattern cannot use \d{1,3} for an octet, because that accepts 999. The range has to be spelled out as an alternation of three shapes, which is what the preset does.
At a glance
- Built in preset
- IPv4
- Octet subpattern
- (?:25[0-5]|2[0-4]\d|[01]?\d\d?)
- Preset flags
- g
- Correctly rejects
- 256.1.1.1 and 999.999.999.999
- Still accepts
- 010.0.0.1, where a leading zero may mean octal elsewhere
- Does not check
- whether the address is routable, private or reserved
In detail
The pattern everyone writes first is \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}, and it matches 999.999.999.999 without hesitation. Digit counts cannot express a numeric range, so the only way to constrain an octet inside the pattern is to enumerate the shapes a number from 0 to 255 can take.
That enumeration is the three branch alternation the preset uses: 25 followed by 0 to 5, or a 2 followed by 0 to 4 and any digit, or an optional leading 0 or 1 followed by one or two digits. Between them the branches cover every value up to 255 and nothing above it, and the branches are ordered longest first so the greedy case is tried before the general one.
The word boundaries at each end do more work than they look like they do. Without them the pattern would find a valid address hiding inside a longer run of digits, and with them the whole of 999.999.999.999 fails: every starting position either begins a three digit run that cannot be followed by a dot, or sits between two digits where there is no boundary at all.
One thing it does allow is a leading zero, so 010.0.0.1 matches. That is a real ambiguity rather than a pedantic one, because some resolvers read a leading zero as an octal prefix and 010 then means eight. If your input can come from a user, either reject leading zeros explicitly or normalise the address before comparing it to anything.
Examples
const octet = "(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)";
const ipv4 = new RegExp("\\b" + [octet, octet, octet, octet].join("\\.") + "\\b", "g");
"Server IPs: 192.168.1.1, 10.0.0.255, 172.16.254.1, invalid 999.999.999.999"
.match(ipv4);
// ["192.168.1.1", "10.0.0.255", "172.16.254.1"]The invalid address produces no match at all, not even a partial one, because of the word boundaries at each end.
const naive = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
naive.test("999.999.999.999"); // true
const octet = /(?:25[0-5]|2[0-4]\d|[01]?\d\d?)/.source;
const strict = new RegExp("^" + [octet, octet, octet, octet].join("\\.") + "$");
strict.test("256.1.1.1"); // false
strict.test("010.0.0.1"); // true, leading zeros are still allowedThings that catch people out
- \d{1,3} cannot express a numeric range, so any IPv4 pattern built on digit counts accepts values above 255.
- Leading zeros are accepted, and some resolvers treat them as octal, so 010.0.0.1 and 10.0.0.1 are not necessarily the same address.
- The preset finds addresses inside text and says nothing about whether they are routable. A private range check or a reserved range check is separate work.
Try it: Regex Tester
Type a pattern and a test string, watch every match highlight as you go, read a plain English breakdown of each token, and check the numbered and named groups it captured.
Regex Tester
LOCAL · Browser-OnlyTest and debug regular expressions with live matching and highlights
Related Regex syntax
All regex syntax →Regex for UUIDs: matching the 8-4-4-4-12 layout
The preset matches the hexadecimal layout of any UUID. Pinning it to version 4 takes two extra character classes, one for the version and one for the variant.
Regex for URLs: finding links without overreaching
The built in URL preset needs a scheme and a dotted host, which rules out localhost and bare ports. It also swallows a full stop at the end of a path.
{n,m}: bounded repetition between two counts
A bounded count is inclusive at both ends and greedy toward the maximum. JavaScript has no {,m} shorthand, and writing one produces literal text.
Regex for hex colours: three, six and eight digit forms
The built in preset matches the three and six digit forms and rejects the four and eight digit ones with alpha. Here is the pattern that covers all four.
Regex for ISO dates: matching YYYY-MM-DD without fake days
The preset rejects month 13 and day 32 with two alternations, and still accepts 2026-02-31. No regex can check a calendar, so parse and compare.
Regex for phone numbers, and why strict ones lose customers
Phone formatting varies by country and by habit. The built in US preset does not even match the parenthesised form in its own sample, which is the lesson.
Tools for this
- Regex Tester Free online regex tester - test and debug regular expressions with live matching and highlights
- Regex Playground Regex playground with live match highlighting, named groups, find and replace, and shareable links