Quick answer
This MAC address regex accepts six pairs of hex digits separated by colons (00:1A:2B:3C:4D:5E) or hyphens (00-1a-2b-3c-4d-5e), plus Cisco's dotted form (0123.4567.89ab). The separator after the first pair is captured in group 1 and the backreference \1 forces every later separator to be the same, so mixed forms like 00:1A-2B:3C:4D:5E fail.
- Tested examples
- Token-by-token breakdown
- Code in 5 languages
- Live tester
How to use the MAC address regex
- 1Copy the pattern with Copy pattern in the tester, or take a ready-made snippet for your language from the code section below.
- 2Check the sample lines in Test string: every line the MAC address regex accepts is highlighted, rejected lines stay plain.
- 3Paste your own values one per line to see which pass, and read Explanation for what each part of the pattern does.
- 4Open the Code tab for JavaScript, Python, Go, C#, PHP or Dart with the pattern translated and escaped.
- 5Validate again on the server: client-side checks give users quick feedback, not security.
How does the MAC address regex work?
Read it left to right. Each piece below matches one part of the input:
[0-9A-Fa-f]{2}The first octet: two hex digits.([:-])Group 1 captures the separator, a colon or a hyphen.(?:[0-9A-Fa-f]{2}\1){4}Four more octets, each followed by the same separator: \1 must equal what group 1 captured.[0-9A-Fa-f]{2}The sixth octet, with no separator after it.|[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}Or the Cisco notation: three groups of four hex digits separated by dots.
Which strings does the MAC address regex match?
The whole input must match, from the first character to the last. Every row below is checked against the pattern by our automated test suite before each release.
| Input | Matches? | Why |
|---|---|---|
00:1A:2B:3C:4D:5E | Yes | Colon-separated, the Linux and macOS style. |
00-1a-2b-3c-4d-5e | Yes | Hyphen-separated, the Windows style. |
0123.4567.89ab | Yes | Cisco dotted notation. |
FF:FF:FF:FF:FF:FF | Yes | The broadcast address. |
00:1A:2B:3C:4D | No | Only five octets. |
00:1A-2B:3C:4D:5E | No | Mixed separators: the backreference refuses the hyphen. |
001A2B3C4D5E | No | No separators (see the variant below). |
00:1G:2B:3C:4D:5E | No | G is not a hex digit. |
00:1A:2B:3C:4D:5E:6F | No | Seven octets (EUI-64 identifiers are a different format). |
How does the backreference keep separators consistent?
([:-]) captures whichever separator appears first. Every later \1 must match the same text, not the same pattern, so once a colon has been seen, a hyphen cannot follow. Without the backreference you would need two near-identical alternatives, one per separator.
Go's RE2 engine does not support backreferences, so the generated Go code will not compile as is. In Go, use two alternatives instead:
^(?:[0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5}|[0-9A-Fa-f]{2}(?:-[0-9A-Fa-f]{2}){5}|[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4})$or call net.ParseMAC, which accepts all three notations and returns the bytes.
How do I accept MAC addresses without separators?
Some inventories and APIs store bare hex like 001A2B3C4D5E. Add it as another alternative, |[0-9A-Fa-f]{12}, or normalize everything to one canonical form before storing: remove :, - and ., uppercase or lowercase it, then reinsert colons every two characters. Comparing normalized strings avoids treating 00-1a-… and 00:1A:… as two devices.
Remember that a MAC address is not a stable identity: phones and laptops randomize it per Wi-Fi network for privacy, which you can spot by the "locally administered" bit (the second hex digit is 2, 6, A or E).
How do I use the MAC address regex in JavaScript, Python, Go, C# and PHP?
The pattern is translated for each engine rather than pasted as is: named groups, anchors, escapes, shorthand classes and flags are adjusted so each snippet behaves as closely to the JavaScript version as its engine allows. Differences that remain (Go's RE2 has no lookarounds or backreferences, for example) are called out under each one. The Code tab in the tester above also exports Dart, and updates as you edit the pattern.
JavaScript and TypeScript
const regex = /^(?:[0-9A-Fa-f]{2}([:-])(?:[0-9A-Fa-f]{2}\1){4}[0-9A-Fa-f]{2}|[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4})$/;
function isValid(value) {
return regex.test(value);
}
console.log(isValid("00:1A:2B:3C:4D:5E"));Python
import re
pattern = re.compile(r"^(?:[0-9A-Fa-f]{2}([:\-])(?:[0-9A-Fa-f]{2}\1){4}[0-9A-Fa-f]{2}|[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4})\Z")
def is_valid(value: str) -> bool:
return pattern.search(value) is not None
print(is_valid("00:1A:2B:3C:4D:5E"))- JavaScript's $ only matches at the very end of the string. Python's $ also matches before a trailing newline, so the pattern uses \Z instead.
Go
package main
import (
"fmt"
"regexp"
)
func main() {
re := regexp.MustCompile(`^(?:[0-9A-Fa-f]{2}([:\-])(?:[0-9A-Fa-f]{2}\1){4}[0-9A-Fa-f]{2}|[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4})$`)
fmt.Println(re.MatchString("00:1A:2B:3C:4D:5E"))
}Go's regexp package (RE2) does not support backreferences like \1 or \k<name>, so this pattern will not compile. Capture the parts and compare them in Go code.
C#
using System;
using System.Text.RegularExpressions;
// The timeout stops a pathological input from hanging the thread (ReDoS).
var regex = new Regex(@"^(?:[0-9A-Fa-f]{2}([:\-])(?:[0-9A-Fa-f]{2}\1){4}[0-9A-Fa-f]{2}|[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4})\z", RegexOptions.None, TimeSpan.FromSeconds(1));
Console.WriteLine(regex.IsMatch("00:1A:2B:3C:4D:5E"));- JavaScript's $ only matches at the very end of the string. .NET's $ also matches before a trailing newline, so the pattern uses \z instead.
PHP
<?php
$pattern = '/^(?:[0-9A-Fa-f]{2}([:\-])(?:[0-9A-Fa-f]{2}\1){4}[0-9A-Fa-f]{2}|[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4})$/D';
function isValid(string $value, string $pattern): bool
{
return preg_match($pattern, $value) === 1;
}
var_dump(isValid('00:1A:2B:3C:4D:5E', $pattern));- The D modifier makes $ match only at the very end, like JavaScript (PCRE's $ also matches before a trailing newline).
Which related regex patterns are useful?
- Regex for IPv4 Addresses: Four octets from 0 to 255, no leading zeros, nothing before or after.
- Regex for IPv6 Addresses: Eight hextets, or fewer with a single :: compression, like 2001:db8::1.
- Regex for UUIDs (Versions 1–8): 8-4-4-4-12 hex layout with a real version digit (1–8) and the standard variant.
- Regex for Hex Color Codes: CSS hex colors in 3, 4, 6 or 8 digits with a leading hash.
Browse all 25 patterns in the regex pattern library, or build your own in the regex tester.
Frequently asked questions
Last updated by the Playxoft engineering team.