Skip to content
d.devtul.fun
中文
Regex · IPv4

IPv4 Regex

A correct IPv4 pattern that bounds every octet to 0-255, and why range checks belong in code, not in a regex.

The pattern

^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$

Matches four dot-separated octets where each is 0-255. It rejects 256, 999, and leading-zero traps only partially; see the notes on octal.

Test this pattern

Edit the text below — matching happens locally, nothing is uploaded.

Variants

Allow leading zeros
^(25[0-5]|2[0-4]\d|1\d\d|[0-9]?\d?)(\.(25[0-5]|2[0-4]\d|1\d\d|[0-9]?\d?)){3}$

Permits 01 and 001. Handy for lax input, dangerous if a parser treats them as octal.

With optional CIDR
^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}(/(3[0-2]|[12]?\d))?$

Adds an optional /0-32 suffix for network notation. Still a shape check, not a route check.

The everyday IPv4 regex

The wrong IPv4 pattern is everywhere because it is short. The right one is a little longer because it actually bounds each number. Here is the version I trust for a single address:

^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}$

Four octets, each separated by a dot, each forced into the range 0 through 255. No more, no less.

Why \d{1,3} is wrong

The tempting shortcut is \d{1,3}(\.\d{1,3}){3}. It looks right and fails badly. A single octet of one to three digits accepts 999, so the pattern happily matches 999.999.999.999, which is not an IP address at all. Worse, it accepts 256.1.1.1 even though 256 is out of range. The moment you care whether the value is a real address, this pattern is lying to you.

PatternAccepts 999.999.999.999Accepts 256.1.1.1Verdict
\d{1,3}(\.\d{1,3}){3}yesyeswrong
[0-9]{1,3}(\.[0-9]{1,3}){3}yesyeswrong
octet-bounded pattern abovenonocorrect

Reading the octet pattern

Each octet is built from mutually exclusive alternatives, tried in order:

  • 25[0-5] covers 250 through 255.
  • 2[0-4]\d covers 200 through 249 (the tens digit is 0-4, the ones digit is anything).
  • 1\d\d covers 100 through 199.
  • [1-9]?\d covers 0 through 99, and by requiring the first digit to be optional-but-non-zero it still permits 0 and 7 while rejecting a bare second zero only where it matters.

The alternation is ordered high-to-low so the engine matches the largest valid number first. The four octets are one group repeated three times with a leading dot, wrapped so nothing extra sneaks in at either end.

The leading-zero trap

Even a correct numeric pattern has a semantic trap: 010.1.1.1. In many C and Unix parsers, a leading zero means octal, so 010 is decimal 8, not 10. The strict pattern above rejects 010 because the octet alternatives start at [1-9]?\d, which forbids a leading zero on multi-digit values. If your downstream code uses a C-style parser, that rejection is exactly what you want. If you accept leading zeros, document it, because the same string means different numbers to different systems.

What the pattern matches and misses

InputExpectedNote
192.168.1.1acceptprivate range, the textbook case
127.0.0.1acceptloopback
256.1.1.1rejectfirst octet out of range
999.999.999.999rejectall octets out of range
01.2.3.4reject in strict formleading zero treated as octal risk

CIDR suffixes and private ranges

A single address is often not enough; you may need a network like 10.0.0.0/8. The variant below adds an optional prefix length:

^(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}(/(3[0-2]|[12]?\d))?$

The (/...)? at the end allows a slash and a number from 0 to 32. That describes a network, not a host, so do not feed it to code that expects one address. For private-range checks, compare the integer form against 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 after parsing, not with a regex.

Why range checks belong in code

The core argument: expressing 0-255 in a regex is ugly and brittle, and it still only proves the text looks like an address. The honest approach splits the string and compares integers:

def is_ipv4(s):
    parts = s.split(".")
    if len(parts) != 4:
        return False
    for p in parts:
        if not p.isdigit() or not 0 <= int(p) <= 255:
            return False
    return True

This is shorter to reason about, rejects 256 and 999 with one comparison, and avoids the octal ambiguity if you also forbid leading zeros explicitly. A regex is fine for a quick grep or a form hint; for anything you will branch on, parse it.

Testing your IPv4 pattern

Run the table cases and add 0.0.0.0, 255.255.255.255, and 1.2.3 (only three octets). If your pattern accepts any of 256.1.1.1, 999.0.0.1, or a three-part string, fix the octet alternation before you trust it. And if the value drives a network decision, convert to integers and compare; let the regex stay a doorman, not the judge.

Octal, decimal, and what your parser assumes

The same four numbers can mean different things depending on the reader. A strict pattern rejects 010.0.0.1 to avoid the octal trap, but if you relax it, know the cost: a C library may read 010 as 8, while a language like Python reads it as 10, and a browser may reject it outright. None of that is visible to a regex. The safe design is to forbid leading zeros in validation, then convert each octet with an explicit base-10 parse. That removes the ambiguity before it reaches a system call, and it keeps a single, predictable meaning for every address you accept.

Frequently asked

What is the correct IPv4 regex?

One that bounds each octet with 25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d, repeated four times. It rejects 256 and 999.

Why is \d{1,3} wrong for IP addresses?

It allows 999 and 256, which are not valid octets, so it matches strings that are not IP addresses.

Are leading zeros allowed in IPv4?

A strict pattern rejects them because 010 can mean octal 8 in C-style parsers. Allow them only if you document the behavior.

Should I validate IP ranges with regex?

No. Parse the four octets to integers and compare against the range; a regex only checks shape.

Related regex guides

Open the full Regex Tester