IPv4 Address Regex
The pattern above correctly matches only valid IPv4 addresses. From the test strings, it matches 192.168.1.1, 10.0.0.255, 172.16.0.1, 255.255.255.0, 8.8.8.8, 0.0.0.0, and 127.0.0.1. It rejects 999.999.999.999, 256.1.1.1, and 1.2.3 (only three octets).
Pattern Breakdown
Each octet uses a three-way alternation to enforce the 0 to 255 range:
| Alternation branch | Range covered | Example matches |
|---|---|---|
25[0-5] | 250 to 255 | 250, 255 |
2[0-4]\d | 200 to 249 | 200, 214, 249 |
[01]?\d\d? | 0 to 199 | 0, 99, 192 |
The alternation is wrapped in a non-capturing group (?:...) and repeated three times with \. between them using {3}, then the final octet repeats the same alternation. Word boundaries \b at both ends prevent the pattern from matching an IP address that appears as a substring of something larger (e.g., pulling 1.2.3.4 out of version_1.2.3.4).
Why the Naive Pattern Fails
\d+\.\d+\.\d+\.\d+ is the first thing most people try. It fails in two directions:
It matches strings that are not valid IP addresses. 999.999.999.999 matches because \d+ places no limit on the numeric value.
It can produce false positives inside longer strings. Without word boundaries, the pattern matches partial fragments in version strings, timestamps, and decimal numbers.
The correct alternation-based pattern handles both problems.
CIDR Notation
To also match CIDR blocks like 192.168.1.0/24, append (\/([0-9]|[1-2]\d|3[0-2]))? to the end of the base pattern (before the final \b). This makes the prefix length optional and validates that it falls between 0 and 32.
IPv6 Pattern
A pattern for the standard 8-group full notation:
([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}
This matches 2001:0db8:85a3:0000:0000:8a2e:0370:7334 but does not handle :: compression, which is the shorthand form that omits consecutive groups of zeros. For example, ::1 (loopback) and 2001:db8::1 are both valid IPv6 addresses that this pattern misses.
A practical approach for logs: use the liberal pattern ([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4} to extract candidates, then validate each candidate with a library function.
Private Range Detection
The three private IPv4 ranges defined by RFC 1918:
| Range | CIDR | Regex prefix |
|---|---|---|
| Class A private | 10.0.0.0/8 | ^10\. |
| Class B private | 172.16.0.0/12 | ^172\.(1[6-9]|2\d|3[01])\. |
| Class C private | 192.168.0.0/16 | ^192\.168\. |
The Class B range (172.16 through 172.31) requires the alternation for the second octet because you cannot express “16 to 31” with a simple character class. Additional special ranges to consider: loopback (127.0.0.0/8), link-local (169.254.0.0/16), and documentation (192.0.2.0/24).
When to Skip Regex
For validating a single IP address submitted by a user, use a language built-in:
# Python
import ipaddress
try:
ipaddress.ip_address("192.168.1.1")
# valid
except ValueError:
# invalid
// Node.js
const net = require('net');
net.isIPv4('192.168.1.1'); // true
net.isIPv4('999.1.1.1'); // false
These functions handle edge cases that regex misses, including leading zeros (some parsers interpret 010 as octal 8) and IPv4-mapped IPv6 addresses. Regex remains the right tool for extracting IP addresses from log files, access logs, or any free-form text where you need to find all occurrences in a larger string.
For generating or verifying hashes of IP addresses in security contexts, the Hash Generator tool on DevBento handles common algorithms without leaving the browser.