28 thoughts on “Regular expressions for IP addresses, CIDR ranges and hostnames

  1. Thanks for these!

    One fix for the IPv4 CIDR – it currently allows for ‘0’ for the mask bits, so 4.4.4.4/0 would be a match.

    It should be:

    ^(?:(?: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]?)(/([1-9]|[1-2]d|3[0-2]))$

    1. Sarmad,

      I’m glad you’ve found the page useful. The research that I’ve done suggests that /0 is in fact a valid CIDR mask. If you have a reference to the contrary, I’d be interested to see it! Cheers!

      Mark

  2. I found that the mask group is evaluated from left to right (as expected) and that when using these to find CIDR addresses the match stops at the first number when there are more.

    For example for 127.0.0.1/32 the match is 127.0.0.1/3.

    Fixed by moving the single digit match to the end.
    (([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(/([1-2]d|3[0-2]|d))

    1. Hi Itamar,

      192.168.0.0 is indeed a valid IP address, so long as it is within a subnets usable address range.

      For example 192.160.0.0/12 would have usable addresses: 192.160.0.1 – 192.175.255.254

  3. The regex for ipv6 and ipv6 cidr accept “derp” as a valid input. It obviously isn’t because ‘p’ is not hex and it is quite short to be ipv6. I don’t currently know how to generate this stuff, but is there a way to fix this?

    1. Pasting the regex in to Regex Pal, I’m not seeing the behaviour that you are seeing. Since the ranges are specified as [0-9A-Fa-f]{1,4} I can’t see how an ‘r’ or ‘p’ would ever be considered part of a match. Can you provide some examples of what you are seeing?

  4. Hey, Sorry, I’m not skilled enough in regex to provide a fix, but when testing your IPv4 CIDR range on RegEx Pal I was not able to get a match on lots of combinations that I think should work, for example: 10.0.0.0/16 or 123.123.0.0/20 where as these are valid as confirmed by http://ipduh.com/ip/cidr/ (I was able to get a match for others such as 123.123.0.0/32 to confirm I may have been using RegEx Pal correctly.) Thoughts?

    1. Hi Mike,

      Good call. It looks like the problem is in the CIDR part after the slash. It should have had “/d” rather than just “d” for the two digit parts. I’ll update the main post in a few moments to fix this. I’ve taken the opportunity to update the rest of the Regex to use /d instead of [0-9].

      1. Thanks Mark, all sorted. Thanks for your page.

        I’m using your regex as the start of a validation function in my python script.

        Its probably worth your readers noting that the regex checks for the formatting which is super great, but will match on things like 123.123.123.123/20 which technically isn’t valid (it should be something like 123.123.112.0/20).

        However I knew what I was looking for, and found it on your page. Thanks!

      2. I’ve actually just switched it all back to using [0-9] throughout since some regex engines seem to want \d and some want just “d”. To avoid confusion, the numeric range seems safer! I have not yet updated the IPv6 regexes though, so I’m wondering if these might be suffering from similar problems.

  5. You should probably escape the period in the CIDR regex. it matches any character, not just the period character right now.

    1. Thanks Al. I’ve updated both the IPv4 and IPv4 CIDR regexs as they were both allowing any character where it should be just “.”.

  6. Hi, first of all thanks for this page :D, saves a lot of time.

    There’s a minor escape issue in your ipv4 CIDR regex, a forward slash is not escaped

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

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

    rewritten with bold (had no idea if bold would work)
    /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/

  7. I have tried your IPv6 CIDR range expression with the following address but it fails to match. I don’t know why but wondered if you have an explanation?

    2620:0:2d0:200::7/32

  8. When using your regex to extract addresses from strings, you find that a subnet of 10.0.0.0/24 is returned as 10.0.0.0/2 because the precedence of the final matching group has single digit matches first. Changing it to this gets around the problem:-

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

    Great work though.

  9. Hi,

    for what is the percent char used in the cidr v6 (%.+)? term used?
    In all regex parser i used it is parsed as the literal % ?!?

    tommes

  10. Your regex for “IPv4 CIDR range” only matches first digit of the mask. e.g. for /32 it would match everything up to “/3”. You should have more specific cases evaluated first. This seems to have fixed the issue for me:

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

  11. Thanks a lot for this work!

    But, both CIDR need a little fix on the netmask. They need to go from all possible matches to least possible matches.
    IPV4 CIDR should end with (3[0-2]|[1-2][0-9]|[0-9])
    IPV6 CIDR should end with (12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9])

Leave a Reply

Your email address will not be published. Required fields are marked *