Regex for an IPv4 address (dotted decimal)

A strict pattern that follows the grammar in RFC 3986 (four decimal numbers from 0 to 255, no leading zeros) and a lenient one that tolerates leading zeros, each with a test table and the reasons you might still prefer a parser.

Strict (RFC 3986 dec-octet)

Rule: RFC 3986 IPv4address: four dec-octet values separated by dots, where dec-octet is 0-9, 10-99, 100-199, 200-249 or 250-255 (no leading zeros).

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

Open in tester Loads the pattern with sample text, JavaScript flavor.

Parts of the pattern

25[0-5]
250 to 255.
2[0-4][0-9]
200 to 249.
1[0-9]{2}
100 to 199.
[1-9]?[0-9]
0 to 9 (a single digit) or 10 to 99 (first digit 1-9, so no leading zero).
(?:octet\.){3}octet
Three octets each followed by a literal dot (escaped, because an unescaped dot means "any character"), then the last octet. ^ and $ require the whole string to be the address.

Test cases

16 of 16 rows agree with the rule.

Test cases for Strict (RFC 3986 dec-octet)
Input Rule says Pattern says Result Note
192.168.1.1 valid match pass Typical private address.
0.0.0.0 valid match pass Syntactically valid. Whether it is a usable host address is a separate question.
255.255.255.255 valid match pass Highest value in every octet.
10.0.0.1 valid match pass Octet 0 is allowed.
1.2.3.4 valid match pass Single digits.
256.1.1.1 invalid no match pass 256 is above 255.
1.2.3 invalid no match pass Only three octets.
1.2.3.4.5 invalid no match pass Five octets.
01.2.3.4 invalid no match pass Leading zero: not a dec-octet in RFC 3986.
1.2.3.04 invalid no match pass Leading zero in the last octet.
1.2.3.999 invalid no match pass 999 is above 255.
1.2.3.4␣ invalid no match pass Trailing space.
1..2.3 invalid no match pass Empty octet.
a.b.c.d invalid no match pass Not digits.
1.2.3.4\n invalid no match pass Trailing newline: rejected by JavaScript's $. PCRE2, Python and Java's $ would accept it (see flavor notes).
0x7f.0.0.1 invalid no match pass Hexadecimal octets are accepted by some C library functions but are not in the RFC 3986 grammar.

Lenient (leading zeros allowed)

Rule: Four decimal numbers of one to three digits, each worth 0 to 255 when read as decimal; leading zeros tolerated.

/^(?:(?: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]?)$/

Open in tester Loads the pattern with sample text, JavaScript flavor.

Parts of the pattern

25[0-5]|2[0-4][0-9]
250-255 and 200-249.
[01]?[0-9][0-9]?
One to three digits where a three-digit number starts with 0 or 1: 0-9, 00-99, 000-199.

Test cases

10 of 10 rows agree with the rule.

Test cases for Lenient (leading zeros allowed)
Input Rule says Pattern says Result Note
192.168.1.1 valid match pass Same as the strict one.
010.001.000.255 valid match pass Leading zeros read as decimal. See the warning below: some software reads 010 as octal 8.
001.2.3.4 valid match pass Three-digit form with leading zeros.
0001.2.3.4 invalid no match pass Four digits in one octet.
256.1.1.1 invalid no match pass 256 is above 255.
1.2.3 invalid no match pass Three octets.
1.2.3.256 invalid no match pass 256 is above 255.
300.1.1.1 invalid no match pass 300 is above 255.
1.2.3.4. invalid no match pass Trailing dot.
1.2.3.-4 invalid no match pass Minus sign.

How it works

RFC 3986 section 3.2.2 defines IPv4address as dec-octet "." dec-octet "." dec-octet "." dec-octet, and dec-octet as one of five alternatives: a single digit, 1-9 followed by a digit (10-99), "1" followed by two digits (100-199), "2" followed by 0-4 and a digit (200-249), or "25" followed by 0-5 (250-255). A regular expression cannot compare numbers, so each numeric range is spelled out as digit shapes. The strict pattern is that grammar translated one alternative at a time.

The order of alternatives matters for how the engine works but not for what it accepts, because the whole string is anchored: the engine may try 25[0-5] first and fall back to [1-9]?[0-9] when the next character is not 0-5, and the final dot or end anchor decides.

The test suite goes beyond the table: for every octet value from 0 to 300 it builds an address with that value in each of the four positions, in plain, one-zero-padded and two-zero-padded form, and compares the pattern with an independent function that parses the numbers. Both variants agree with their own rule in every case.

Known false positives

  • Syntax only: 0.0.0.0, 255.255.255.255, 127.0.0.1 and multicast values such as 224.0.0.1 all pass. The pattern says the text looks like an IPv4 address, not that the address can be assigned to a host.
  • The lenient variant accepts 010.001.000.255. Some software reads a leading zero as octal. RFC 3986 notes that a few libraries (inet_aton is its example) interpret components as decimal, octal or hexadecimal depending on their prefix, which is why the strict form rejects leading zeros.

Known false negatives

  • Both patterns reject shorthand forms that some C library calls accept, such as 127.1 or a single 32-bit number, and reject hexadecimal or octal octets. The RFC 3986 grammar allows only the four-octet dotted-decimal form.
  • IPv6 addresses, including IPv4-mapped ones like ::ffff:1.2.3.4, are rejected: this is only the IPv4 form. Addresses with a prefix length (10.0.0.0/8) are also rejected.

Flavor notes

  • Dots must be escaped. An unescaped . matches any character, so ^1.2.3.4$ also accepts 1x2y3z4. Inside a character class a dot is literal.
  • Trailing newline: PCRE2 (by default), Python and Java let $ match before a final newline, so "1.2.3.4" followed by a line feed passes there but fails in JavaScript and Go. Use \z (PCRE2, Go), \Z or fullmatch() (Python) or matches() (Java) when you mean the whole string. The cross-engine tests here use \z.
  • [0-9] and [1-9] are used instead of \d so that the pattern means the same in Python (where \d is any Unicode decimal digit) and in PCRE2 with UCP.
  • The pattern uses only features present in JavaScript, PCRE2 and Go/RE2. It is tested on those three real engines here. Python and Java are notes only on this site and nothing is executed on them.

Why not regex here?

If the address will be used to make a decision, such as an allow-list or a block-list, convert it to its numeric form and compare numbers rather than text. RFC 3986 section 7.4 makes this point: system routines such as inet_aton() accept more formats than the URI grammar (three-part and two-part forms, a single 32-bit number, octal and hexadecimal parts), so filtering on the string literal can be bypassed, and it advises converting literals to numeric form and filtering on the numeric value.

Use the regex when you only need to find address-shaped text in a log line or to give a form field fast feedback. Drop the anchors and add \b boundaries to search inside longer text; be aware that 1.2.3.4.5 then contains a match.

How to use

  1. Copy the pattern for the variant that fits your rule. Variants differ in strictness, and the rule line says exactly what each accepts.
  2. Check how it is anchored: the patterns use ^ and $ to test a whole string. To find the same thing inside longer text, remove the anchors (and add word-boundary or lookaround checks) and re-run the cases.
  3. If your language is not JavaScript, read Flavor notes and change $ to \z or use a full-match function.
  4. Open it in the tester to see the explanation of each token and try your own inputs.

Worked examples

Reject octal-looking input

Run 010.0.0.1 through the strict pattern: it fails, which forces the caller to decide whether the user meant decimal 10 or octal 8.

Find addresses in a log line

Remove ^ and $ and wrap with \b: \b(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}\b. Open the tester to see it highlight addresses in a paragraph.

Limits & gotchas

  • The pattern checks text shape. It cannot know whether an address is routable, private, reserved or in use.
  • Using \b to find addresses inside text treats 1.2.3.4.5 as containing 1.2.3.4. Add a lookahead such as (?![.0-9]) in JavaScript and PCRE2 if that matters; Go has no lookahead.

FAQ

Why does the strict pattern reject 192.168.001.001?

RFC 3986 defines each octet without leading zeros, and some software treats a leading zero as octal. Rejecting them makes the ambiguity visible. Use the lenient variant if you must accept padded input and you know it will be read as decimal.

Does the pattern validate subnet masks or CIDR notation?

No. 10.0.0.0/8 fails because of the /8, and the pattern does not know which masks are legal. Match the address and the prefix length separately.

Why isn't \d used for the digits?

In Python str patterns \d matches any Unicode decimal digit, and PCRE2 does too when UCP is on. [0-9] is the same everywhere.

Can I use this pattern in Go?

Yes. It uses no lookaround or backreferences, so Go's regexp (which follows RE2 syntax) accepts it, and this site's tests run it on a real Go regexp engine. Use \z rather than $ if you want a guaranteed whole-string match with a trailing newline in the input; Go's $ already means end of text.

Sources

  1. IETF: RFC 3986: URI Generic Syntax Used for: dec-octet and IPv4address, segment and pchar grammar, pct-encoded, unreserved, sub-delims.
  2. MDN: Input boundary assertion: ^, $ Used for: ^ and $ are the start and end of input, or of each line with the m flag.
  3. PCRE2: pcre2pattern Used for: Syntax and semantics: groups, named groups, lookbehind rules, atomic groups, possessive quantifiers, \d \s \w with and without UCP, dollar and newline handling, \A \Z \z.
  4. Python docs: re: Regular expression operations (3.13) Used for: Syntax, (?P<name>), atomic groups and possessive quantifiers (3.11+), fixed-length lookbehind, Unicode \d \s \w, $ before trailing newline, \A \Z, re.sub replacement syntax, inline flags at start only (3.11+).
  5. Oracle (Java SE 21): java.util.regex.Pattern Used for: Construct table, \d \s \w without UNICODE_CHARACTER_CLASS, possessive and atomic constructs, named groups, line terminators and $, \A \Z \z.
  6. Go: regexp/syntax Used for: Full syntax table; no lookaround or backreferences; \d \s \w are ASCII-only; $ is \z; (?P<name>) and (?<name>); repetition limit 1000; \A and \z.
  7. Google RE2: RE2 Syntax (wiki) Used for: RE2 syntax with explicit NOT SUPPORTED markers: lookaround, backreferences, possessive quantifiers, \Z, atomic groups.
  8. MDN: Character class escape: \d, \D, \w, \W, \s, \S Used for: \d is [0-9]; \w is letters, digits and underscore; \s is whitespace plus line terminators.

Every document above was opened and read on 2026-10-02. Documentation changes; if a page here disagrees with the current docs, trust the docs and tell us.