/^/
HardNetworking8 min read

IPv6 CIDR Notation Regex

Validates an IPv6 address written in CIDR (Classless Inter-Domain Routing) notation, such as 2001:db8::/32 or ::1/128, including the zero-compressed :: shorthand and a prefix length from 0 to 128.

#ipv6#cidr#networking#subnet#validation#ip-address

Regex Pattern

^(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?:(?::[0-9A-Fa-f]{1,4}){1,6})|:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:))\/(?:12[0-8]|1[01][0-9]|[1-9]?[0-9])$

Pattern Breakdown

Hover over a token to see what it does.

^(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?:(?::[0-9A-Fa-f]{1,4}){1,6})|:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:))\/(?:12[0-8]|1[01][0-9]|[1-9]?[0-9])$
TokenMeaning
(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}Fully expanded form: eight groups of 1-to-4 hex digits separated by colons, with no :: compression
(?:[0-9A-Fa-f]{1,4}:){1,7}:Compressed form where the address ends in ::, meaning the remaining groups are all zero
(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}One of several branches handling :: compressed somewhere in the middle of the address, with a different split point per branch
:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:)Handles addresses that start with :: (leading zero groups), including the all-zeros address :: itself
\/Literal slash separating the address from the CIDR prefix length
12[0-8]|1[01][0-9]|[1-9]?[0-9]Prefix length from 0 to 128: 120-128, 100-119, or 0-99, split into three numeric ranges
$Anchors the match to the end of the string

Detailed Explanation

What it does

This pattern validates a complete IPv6 address in CIDR notation: the address portion (in any of its valid textual forms, including zero-compression with ::) followed by a slash and a subnet prefix length between 0 and 128.

Why it works

IPv6 addresses have eight 16-bit groups, but the standard allows at most one run of consecutive all-zero groups to be collapsed into ::. Because that collapse can happen at the start, middle, end, or not at all, the pattern is built as an alternation of branches, each one fixing how many groups appear before and after the ::, or requiring all eight groups with no compression. The prefix length is validated with three numeric alternatives instead of a single range quantifier because regex has no built-in way to express 'a number between 0 and 128' directly.

Common use cases

  • Validating subnet definitions in firewall rules, routing tables, or network ACLs
  • Parsing IPv6 CIDR blocks from infrastructure-as-code configuration files
  • Checking user input in a network management dashboard where operators enter subnets manually
  • Filtering log entries or CSV exports to find lines describing IPv6 subnets

Edge cases

  • The all-zeros address with maximum prefix, ::1/128, is matched
  • The default route ::/0 is matched, representing the entire IPv6 address space
  • A fully expanded address with no compression, like 2001:0db8:85a3:0000:0000:8a2e:0370:7334/64, is matched
  • A prefix length of 129 or higher is rejected since valid IPv6 prefixes only go up to 128
  • An address with more than one :: compression, like 2001:db8::1::2/64, is rejected because only one branch of the alternation can match at a time and none permit two double-colons
  • A missing prefix length entirely, like 2001:db8::1 with no slash and number, is rejected since the /prefix suffix is mandatory in this pattern

Limitations

  • Does not support embedded IPv4-mapped IPv6 addresses like ::ffff:192.168.1.1
  • Does not verify that the host bits beyond the prefix length are actually zero, so 2001:db8::1/32 passes even though a strict network address would require the trailing bits to be clear
  • Does not support zone IDs (scope identifiers) like fe80::1%eth0
  • The alternation is long and can be harder to maintain than a small parsing function, though it remains linear-time and safe from catastrophic backtracking

Interactive Tester

Edit the pattern or text below — matching runs live in your browser.

2001:db8::/32 ::1/128 ::/0

Test Cases

Editable — add your own inputs to see if they pass.

InputExpectedResult
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass
Pass

Language Variants

Production-ready examples in 12 languages.

const ipv6CidrRegex = /^(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?:(?::[0-9A-Fa-f]{1,4}){1,6})|:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:))\/(?:12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;
console.log(ipv6CidrRegex.test('2001:db8::/32')); // true

Common Mistakes

Writing a single simplified branch like ([0-9A-Fa-f]{1,4}:){1,7}[0-9A-Fa-f]{0,4} and expecting it to fully validate :: compression rules

Fix: Use a full alternation with one branch per valid position of the :: compression, since a single simplified pattern cannot correctly enforce that only one compression run is allowed

Forgetting the prefix length range check and using \d{1,3} for the CIDR suffix, which lets invalid values like /200 pass

Fix: Split the prefix length into explicit numeric ranges (0-99, 100-119, 120-128) since regex has no native numeric comparison

Assuming this pattern also validates that host bits past the prefix length are zero

Fix: Perform a separate bitwise check in application code after parsing the address if strict network-address validation is required

Performance Notes

  • The alternation has eight mutually exclusive branches, and most engines try each in order, so worst-case matching is still linear in the length of a single address string, not exponential
  • Anchoring with ^ and $ prevents the engine from retrying the entire alternation at multiple starting offsets
  • Because each branch uses bounded quantifiers ({1,4}, {1,7}, etc.) rather than unbounded nested repetition, this pattern does not exhibit catastrophic backtracking even on malformed long input

Browser Compatibility

EngineSupportedNotes
ChromeYes
FirefoxYes
SafariYes
EdgeYes
Node.jsYesLong alternations like this compile without issue but are easier to maintain when built from smaller named sub-patterns