Quick answer
A domain name regex that follows the DNS rules is ^(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9-]{1,59})$. Each label is 1 to 63 letters, digits or hyphens without a hyphen at either end, the whole name is at most 253 characters, and the top-level domain is letters or a Punycode xn-- label. It matches example.com and sub.example.co.uk.
- Tested examples
- Token-by-token breakdown
- Code in 5 languages
- Live tester
How to use the domain name 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 domain name 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 domain name regex work?
Read it left to right. Each piece below matches one part of the input:
^(?=.{1,253}$)Lookahead: the whole name is at most 253 characters, the DNS limit for a written hostname.[A-Za-z0-9]A label starts with a letter or digit.(?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?Optionally up to 61 middle characters (hyphens allowed) and a final letter or digit, for at most 63 per label.\.A dot ends each label before the top-level domain.(?:…)+One or more labels: example., sub.example., and so on.(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9-]{1,59})The top-level domain: 2–63 letters, or an internationalized one in Punycode such as xn--p1ai (.рф).$End of the string.
Which strings does the domain name 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 |
|---|---|---|
example.com | Yes | A registered domain and a top-level domain. |
sub.example.co.uk | Yes | Any number of labels. |
my-site.io | Yes | Hyphens inside a label are fine. |
xn--bcher-kva.example | Yes | bücher.example in Punycode. |
example.xn--p1ai | Yes | An internationalized top-level domain (.рф). |
a.co | Yes | Single-character labels are allowed. |
example | No | No top-level domain. |
-example.com | No | A label cannot start with a hyphen. |
example-.com | No | Or end with one. |
exa_mple.com | No | Underscores are not valid in hostnames. |
example..com | No | Empty label between two dots. |
example.c | No | Top-level domains have at least two letters. |
example.123 | No | All-numeric top-level domains do not exist. |
What are the rules for a valid domain name?
They come from RFC 1035 and RFC 1123, and each maps to one part of the pattern:
| Rule | Where in the regex |
|---|---|
| Labels are 1–63 characters | [A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])? |
| Letters, digits and hyphens only | the character classes |
| No hyphen at the start or end of a label | first and last class exclude - |
| At most 253 characters in total | (?=.{1,253}$) |
| Case does not matter | uppercase is allowed everywhere |
Underscores are legal in some DNS records (_dmarc.example.com, SRV records) but not in hostnames, so they are rejected here. If you validate DNS record names rather than hosts, add _ to the label classes.
How do I handle internationalized domain names?
Browsers display bücher.example, but DNS only carries ASCII: the name is converted to Punycode (xn--bcher-kva.example) by the IDNA rules. Validate the converted form:
const ascii = new URL(`https://${input}`).hostname; // "xn--bcher-kva.example"The URL parser does the conversion and lowercases the name, so you can run this regex on the result. A domain that matches the pattern is well-formed, not registered: check DNS (or WHOIS) if you need to know it exists.
Go users: RE2 has no lookahead, so check len(name) <= 253 in code and drop (?=.{1,253}$) from the pattern.
How do I use the domain name 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 = /^(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?\.)+(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9-]{1,59})$/;
function isValid(value) {
return regex.test(value);
}
console.log(isValid("example.com"));Python
import re
pattern = re.compile(r"^(?=.{1,253}\Z)(?:[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.)+(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9\-]{1,59})\Z")
def is_valid(value: str) -> bool:
return pattern.search(value) is not None
print(is_valid("example.com"))- JavaScript's . also refuses \r, \u2028 and \u2029; in Python it only refuses \n.
- 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(`^(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.)+(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9\-]{1,59})$`)
fmt.Println(re.MatchString("example.com"))
}Go's regexp package (RE2) does not support lookahead or lookbehind, so this pattern will not compile. Match more broadly and check the extra condition in Go code instead.
- JavaScript's . also refuses \r, \u2028 and \u2029; in Go it only refuses \n.
C#
using System;
using System.Text.RegularExpressions;
// The timeout stops a pathological input from hanging the thread (ReDoS).
var regex = new Regex(@"^(?=.{1,253}\z)(?:[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.)+(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9\-]{1,59})\z", RegexOptions.None, TimeSpan.FromSeconds(1));
Console.WriteLine(regex.IsMatch("example.com"));- JavaScript's . also refuses \r, \u2028 and \u2029; in .NET it only refuses \n.
- 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 = '/^(?=.{1,253}$)(?:[A-Za-z0-9](?:[A-Za-z0-9\-]{0,61}[A-Za-z0-9])?\.)+(?:[A-Za-z]{2,63}|xn--[A-Za-z0-9\-]{1,59})$/D';
function isValid(string $value, string $pattern): bool
{
return preg_match($pattern, $value) === 1;
}
var_dump(isValid('example.com', $pattern));- JavaScript's . also refuses \r, \u2028 and \u2029; in PHP (PCRE) it only refuses \n.
- 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 URL Validation: http and https links with a real domain, optional port, path, query and fragment.
- Regex for Email Validation: Practical email check: dotted local part, real domain labels and a letters-only top-level domain.
- 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.
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.