Match an IPv4 address
Pull IPv4 addresses out of logs and config files with a short, readable pattern — and know exactly when you need the stricter octet-checking version.
Last updated
Matches
- 192.168.1.42
- 8.8.8.8
- 172.16.254.3
Doesn’t match
- 192.168.1
- 192.168..1
- abc.def.ghi.jkl
Why it’s written this way
Three repetitions of "one to three digits and a dot" followed by a final octet, wrapped in word boundaries so 192.0.2.4 inside 192.0.2.45 cannot half-match.
This is the extraction form: short, readable, and right for grep-style work over logs where the data is already machine-written and 999.999.999.999 simply does not occur.
Edge cases to know
- →It accepts out-of-range octets like 256 — each position is just \d{1,3}. Validating user input needs the long form: \b(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\b.
- →Version strings like 1.2.3.4 match too — an IP and a four-part version are the same shape — and a five-part string like 1.2.3.4.5 still yields its first four octets as a match. Filter by context if your text mixes them.
- →IPv6 is a different pattern entirely.
Part by part
- \b
- A word boundary at each end, so the address has to stand alone rather than sit inside a longer run of digits and dots.
- (?:\d{1,3}\.){3}
- Three repetitions of "one to three digits then a dot" — the first three octets and their separators, in one non-capturing group.
- \d{1,3}
- The fourth octet, written separately because it is the only one with no dot after it.
Common mistakes
\d+\.\d+\.\d+\.\d+Dropping the digit limit and the boundaries matches four-part version numbers, build identifiers and timestamps just as happily as addresses, so a changelog full of 1.2.3.4 becomes a list of false positives.
\b(?:\d{1,3}\.){3}\d{1,3}\b(?:\d{1,3}\.){3}\d{1,3}Without the boundaries the pattern finds a valid-looking address inside "10.0.0.1234", reporting 10.0.0.123 and silently dropping the digit that made it wrong.
\b(?:\d{1,3}\.){4}\d{1,3}\bFour repetitions plus a trailing octet describes a five-part address. It is an easy off-by-one because the group contains both a number and its separator.
Questions
Does this reject an octet larger than 255?
No — and that is its main limitation. 999.999.999.999 matches, because the pattern checks the shape rather than the value. It is built for pulling addresses out of logs, where the input is already machine-generated.
How do I validate the range strictly?
Replace each octet with the alternation (25[0-5]|2[0-4]\d|[01]?\d?\d), which spells out 250-255, 200-249 and 0-199 as separate branches. The result is far longer and worth it only when you are validating input rather than scanning text.
Should I just split on dots instead?
For validation, usually yes. Splitting on the dot and checking that you have four parts, each numeric and each between 0 and 255, is easier to read and to correct later than a regex encoding the same rules.
Does it match IPv6 addresses?
No. IPv6 uses colon-separated hexadecimal groups with an optional double colon for compression, which is a completely different shape and needs its own pattern rather than an extension of this one.
What about an address with leading zeros, like 010.0.0.1?
It matches, and that is worth knowing rather than shrugging at. Several networking libraries read a zero-padded octet as octal, so 010 becomes 8 and the address resolves somewhere other than it appears to. If you are matching addresses to make a security decision, reject leading zeros explicitly instead of normalising them.