IPv6 Address Regex
Match IPv6 addresses written in full or with the :: shorthand for a run of zero groups. Each alternative covers one place the :: can sit.
One case per line; the m flag makes ^ and the end anchor work line by line.
Link optionspattern only
The address bar holds the pattern, flags and replacement, so Copy link shares them. Your test text stays out of it unless you include it (up to 2,000 characters), because it may be private. Nothing is sent to a server.
Matches
7matches
The first is “2001:db8::1”, at position 0.
- Groups
- none
- Characters matched
- 110 of 221
What the pattern meansHover or tap a part to see it in the pattern and what it matched
How this works: Method, 5 sources, Checked against 1 worked example,
How this works
Method
Your pattern runs in your own browser’s JavaScript engine, in a background worker that is stopped after 1.5 seconds, so a pattern that backtracks catastrophically can’t freeze the page. The explanation comes from our own parser of the ECMAScript pattern grammar, checked against the engine; what each part matched is found by wrapping that part in one more group and running the pattern again. Conversions to other flavours only rewrite the syntax and list what their documentation says works differently.
Sources
How it’s tested
One worked example for this page is checked by automated tests before every release: given the inputs, the tool must show the expected answer.
Changes
- First published, with a library of 19 common patterns and their test cases.
Worked example
Take 2001:db8::1, the first case in the tester. Reading the pattern left to right, each part takes its share of the text:
^The start of the text (matches a position, no characters)(?:(?:[0-9a-f]{1,4}:){7}[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,7}:|(?:[0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,5}(?::[0-9a-f]{1,4}){1,2}|(?:[0-9a-f]{1,4}:){1,4}(?::[0-9a-f]{1,4}){1,3}|(?:[0-9a-f]{1,4}:){1,3}(?::[0-9a-f]{1,4}){1,4}|(?:[0-9a-f]{1,4}:){1,2}(?::[0-9a-f]{1,4}){1,5}|[0-9a-f]{1,4}:(?::[0-9a-f]{1,4}){1,6}|:(?:(?::[0-9a-f]{1,4}){1,7}|:))A group (not captured), either of nine alternatives: matches 2001:db8::1$The end of the text (matches a position, no characters)
How the IPv6 regex handles the :: shorthand
An IPv6 address is eight groups of one to four hex digits, separated by colons. The building block is [0-9a-f]{1,4}, and the i flag lets upper-case digits through. The first alternative is the full form: seven groups each followed by a colon, then an eighth.
The hard part is ::, which stands for one or more groups of zeros and may appear only once. A regex cannot count the groups on both sides of it, so the pattern lists every split: one to seven groups before it and none after, one to six before and one after, and so on down to the final alternative, where the address starts with ::.
Each alternative bounds its counts so the total stays under eight, which is how 2001:db8:0:0:0:0:0:0:1 with nine groups fails. A second :: can never fit any alternative, because each one allows exactly one double colon.
- RFC 4291 writes an IPv6 address as eight 16-bit pieces of one to four hex digits, and says the :: shorthand for zero groups can only appear once in an address. Source: RFC 4291, IPv6 addressing architecture, section 2.2.
- RFC 5952 recommends a canonical text form: leading zeros suppressed, :: used to its maximum but never for a single zero group, and hex letters in lower case. Source: RFC 5952, IPv6 address text representation.
Test cases
Every case runs as an automated test of this page’s pattern, so the table can’t drift from what the pattern really does.
| Text | Result | Why |
|---|---|---|
| 2001:db8::1 | Passes | documentation prefix, compressed |
| 2001:0db8:0000:0000:0000:ff00:0042:8329 | Passes | all eight groups written in full |
| ::1 | Passes | the loopback address |
| :: | Passes | the unspecified address, all zeros |
| fe80::1ff:fe23:4567:890a | Passes | a link-local address |
| 2001:DB8:0:0:8:800:200C:417A | Passes | upper case and single zeros |
| 1:: | Passes | the :: at the end |
| 2001:db8::1::2 | Fails | two :: in one address |
| 2001:db8:0:0:0:0:0:0:1 | Fails | nine groups |
| 12345::1 | Fails | a group of five hex digits |
| 2001:db8::g1 | Fails | g is not a hex digit |
| 2001:db8:::1 | Fails | three colons in a row |
| ::ffff:192.0.2.128 | Fails | the embedded IPv4 form is not covered |
| fe80::1%eth0 | Fails | a zone index after % |
What it doesn’t check
- The mixed form with an IPv4 address in the last 32 bits, such as ::ffff:192.0.2.128, is valid IPv6 but not matched here; add an alternative for it if you handle IPv4-mapped addresses.
- Zone indexes such as %eth0 on link-local addresses are rejected; strip them before testing.
- It accepts non-canonical spellings such as leading zeros and upper case, which are valid input but not how RFC 5952 says to print an address.
- Brackets around an address, as in a URL like http://[2001:db8::1]:8080/, must be removed first.
The same pattern in other languages
Converted automatically from the JavaScript version; the tester above always runs JavaScript.
| Flavour | Pattern | Notes |
|---|---|---|
| Python | (?i)^(?:(?:[0-9a-f]{1,4}:){7}[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,7}:|(?:[0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,5}(?::[0-9a-f]{1,4}){1,2}|(?:[0-9a-f]{1,4}:){1,4}(?::[0-9a-f]{1,4}){1,3}|(?:[0-9a-f]{1,4}:){1,3}(?::[0-9a-f]{1,4}){1,4}|(?:[0-9a-f]{1,4}:){1,2}(?::[0-9a-f]{1,4}){1,5}|[0-9a-f]{1,4}:(?::[0-9a-f]{1,4}){1,6}|:(?:(?::[0-9a-f]{1,4}){1,7}|:))\Z | |
| PCRE | (?i)^(?:(?:[0-9a-f]{1,4}:){7}[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,7}:|(?:[0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,5}(?::[0-9a-f]{1,4}){1,2}|(?:[0-9a-f]{1,4}:){1,4}(?::[0-9a-f]{1,4}){1,3}|(?:[0-9a-f]{1,4}:){1,3}(?::[0-9a-f]{1,4}){1,4}|(?:[0-9a-f]{1,4}:){1,2}(?::[0-9a-f]{1,4}){1,5}|[0-9a-f]{1,4}:(?::[0-9a-f]{1,4}){1,6}|:(?:(?::[0-9a-f]{1,4}){1,7}|:))\z | |
| Go | (?i)^(?:(?:[0-9a-f]{1,4}:){7}[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,7}:|(?:[0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,5}(?::[0-9a-f]{1,4}){1,2}|(?:[0-9a-f]{1,4}:){1,4}(?::[0-9a-f]{1,4}){1,3}|(?:[0-9a-f]{1,4}:){1,3}(?::[0-9a-f]{1,4}){1,4}|(?:[0-9a-f]{1,4}:){1,2}(?::[0-9a-f]{1,4}){1,5}|[0-9a-f]{1,4}:(?::[0-9a-f]{1,4}){1,6}|:(?:(?::[0-9a-f]{1,4}){1,7}|:))$ | |
| Java | (?i)^(?:(?:[0-9a-f]{1,4}:){7}[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,7}:|(?:[0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,5}(?::[0-9a-f]{1,4}){1,2}|(?:[0-9a-f]{1,4}:){1,4}(?::[0-9a-f]{1,4}){1,3}|(?:[0-9a-f]{1,4}:){1,3}(?::[0-9a-f]{1,4}){1,4}|(?:[0-9a-f]{1,4}:){1,2}(?::[0-9a-f]{1,4}){1,5}|[0-9a-f]{1,4}:(?::[0-9a-f]{1,4}){1,6}|:(?:(?::[0-9a-f]{1,4}){1,7}|:))\z | |
| .NET | (?i)^(?:(?:[0-9a-f]{1,4}:){7}[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,7}:|(?:[0-9a-f]{1,4}:){1,6}:[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){1,5}(?::[0-9a-f]{1,4}){1,2}|(?:[0-9a-f]{1,4}:){1,4}(?::[0-9a-f]{1,4}){1,3}|(?:[0-9a-f]{1,4}:){1,3}(?::[0-9a-f]{1,4}){1,4}|(?:[0-9a-f]{1,4}:){1,2}(?::[0-9a-f]{1,4}){1,5}|[0-9a-f]{1,4}:(?::[0-9a-f]{1,4}){1,6}|:(?:(?::[0-9a-f]{1,4}){1,7}|:))\z |
Sources
Frequently Asked Questions
Why is the IPv6 regex so long?
The double colon can stand for any number of zero groups, and a regex cannot count across it. The only way to keep the total at eight groups is to list each position the double colon can take, which is what the alternatives do.
Does this accept upper-case IPv6 addresses?
Yes, through the i flag. Upper case is valid input, although the recommended printed form uses lower case.
How do I validate an IPv6 address without a regex?
In Node.js, net.isIPv6() returns true for valid addresses, including the IPv4-mapped form. In the browser, wrapping the address in brackets and parsing it with new URL() also checks it.