PatternRegex

Match a domain name

Pick domain names like sub.example.co.uk out of free text, allowing the hyphens real hostnames use while still requiring at least one dot.

Last updated

Patterngi
\b[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+\b
Open in Regex Tester & Builder

Matches

  • Visit example.com for details
  • sub-domain.example.co.uk works too
  • reach us at name@example-server.io please

Doesn’t match

  • localhost has no dot at all
  • just_underscores_here nothing

Why it’s written this way

Each label is [a-z0-9]([a-z0-9-]*[a-z0-9])? — it must start and end with a letter or digit, with hyphens allowed only in the middle, which is exactly how DNS labels are defined (no leading or trailing hyphen). The label group then repeats after a literal dot one or more times via (\.[a-z0-9]...)+, so multi-level names like a.b.co.uk match in one pass.

The i flag makes the letter ranges case-insensitive without needing A-Z alongside a-z everywhere, since real-world hostnames show up in both cases in logs and copy-pasted text.

Edge cases to know

  • →There is no TLD list, so made-up endings like .zzz match just as happily as .com — this checks shape, not registration.
  • →Because it is unanchored, it matches the domain portion inside an email address too — reach@example.com will highlight example.com, which is usually what you want but is worth knowing.
  • →It does not handle internationalized domain names (IDN) written as raw Unicode — only ASCII/punycode labels.

Part by part

[a-z0-9]
A label must start with a letter or digit — never a hyphen. This is a real DNS rule, not a stylistic choice.
([a-z0-9-]*[a-z0-9])?
The middle of a label, optionally, ending again on a letter or digit. The structure is what forbids a trailing hyphen while allowing internal ones.
(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+
One or more further dot-separated labels. The + is what requires at least one dot, so a bare hostname does not match.

Common mistakes

  • [a-z0-9.-]+

    A flat character class accepts leading hyphens, trailing hyphens, doubled dots and a string of dots on its own. Every rule that makes a domain a domain lives in the structure, not in the alphabet.

    \b[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+\b
  • \b[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*\b

    A star instead of a plus makes the dotted part optional, so any single word in the text becomes a domain. It is a one-character change that turns a specific pattern into one that matches prose.

  • \b[a-z0-9-]+(\.[a-z0-9-]+)+\b

    Simplifying each label to one class readmits the leading and trailing hyphen that the nested form exists to forbid — so -bad-.example.com matches.

Questions

Does it enforce the 63-character limit on a label?

No, nor the 253-character total. Both are real DNS limits and both are easier to check after matching than to encode as a quantifier, especially since exceeding them is rare in practice.

Will it match a domain inside an email address or URL?

Yes — it has no idea what surrounds it. Scanning "mail bob@example.com now" finds example.com. Match the larger structure first when you need the domain in context rather than every domain-shaped substring.

Does it accept internationalised domains?

Only as punycode, such as xn--80ak6aa92e.com, because the classes are ASCII. Natively written unicode domains will not match, so normalise before testing if you expect them.

Does a match mean the domain is registered?

No. It confirms the name is well-formed, nothing more. notarealdomain.example matches perfectly and resolves nowhere — only a DNS lookup answers the question you are actually asking.

Related in Patterns