Subdomain Regex
Validates a hostname that includes at least one subdomain label in front of a base domain and top-level domain, such as www.example.com or api.staging.example.co.uk.
Regex Pattern
^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.){2,}[a-zA-Z]{2,}$Pattern Breakdown
Hover over a token to see what it does.
| Token | Meaning |
|---|---|
| ^ | Anchors the match to the start of the string |
| [a-zA-Z0-9] | Each label must start with a letter or digit |
| ([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])? | Optional middle and ending characters of a label, allowing hyphens but never as the first or last character |
| \. | Literal dot separating each label |
| ){2,} | The label-plus-dot group must repeat at least twice, guaranteeing a subdomain plus a base domain before the TLD |
| [a-zA-Z]{2,} | Top-level domain of at least two letters, with no digits or hyphens |
| $ | Anchors the match to the end of the string |
Detailed Explanation
What it does
This pattern validates that a hostname string has at least three dot-separated parts: one or more subdomain labels, a base domain label, and a top-level domain. It rejects bare domains like example.com because they only contain a base domain and TLD without a subdomain.
Why it works
The repeated group ([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.){2,} requires the label-and-dot sequence to occur two or more times before the final TLD segment. A bare domain like example.com only produces one such repetition ('example.'), so the {2,} quantifier forces at least a subdomain label ('www.') plus the base domain label ('example.') to appear, with 'com' left over as the TLD.
Common use cases
- Detecting whether a hostname is a subdomain of a multi-tenant SaaS platform, like tenant.app.example.com
- Routing requests based on whether the Host header includes a subdomain segment
- Validating custom subdomain input in a signup flow, such as choosing a company's subdomain
- Filtering DNS records to find CNAME or A records that represent subdomains rather than apex domains
Edge cases
- A deeply nested hostname like a.b.c.example.com matches because the group can repeat more than twice
- Country-code second-level domains like example.co.uk are matched, with 'example' and 'co' both counted as label repetitions and 'uk' as the TLD
- Numeric-looking labels like 192.168.1.1 fail because the final TLD segment requires letters only, correctly excluding IPv4 addresses
- A hostname with an empty label from a double dot, like example..com, fails to match because empty strings cannot satisfy the label pattern
Limitations
- Does not consult the Public Suffix List, so it cannot tell whether 'co' in example.co.uk is really part of the TLD or a subdomain
- Does not validate total hostname length limits (253 characters) or punycode-encoded internationalized domains
- Cannot distinguish a subdomain that is a live DNS record from one that merely matches the string shape
- Labels starting or ending with a hyphen are correctly rejected, but the regex does not check for other reserved or invalid label sequences
Interactive Tester
Edit the pattern or text below — matching runs live in your browser.
Test Cases
Editable — add your own inputs to see if they pass.
| Input | Expected | Result | |
|---|---|---|---|
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass | |||
| Pass |
Language Variants
Production-ready examples in 12 languages.
const subdomainRegex = /^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.){2,}[a-zA-Z]{2,}$/;
console.log(subdomainRegex.test('www.example.com')); // trueCommon Mistakes
Using {1,} instead of {2,} on the repeated label group, which lets bare domains like example.com incorrectly pass as having a subdomain
Fix: Require at least two label-and-dot repetitions so a subdomain and a base domain are both present before the TLD
Assuming this pattern can identify the 'real' registrable domain in cases like example.co.uk
Fix: Use a Public Suffix List library when you need to distinguish true subdomains from multi-part country-code TLDs
Allowing labels to start or end with a hyphen by using a plain [a-zA-Z0-9-]+ character class for the whole label
Fix: Split the label into first character, optional middle-and-last group, so hyphens are only allowed in the interior
Performance Notes
- The label group has a bounded {0,61} repetition matching the real DNS label length limit, which keeps backtracking bounded
- Anchoring with ^ and $ prevents scanning for a match at multiple offsets in a longer string
- The outer {2,} quantifier on a group containing alternation-free, bounded sub-patterns avoids catastrophic backtracking in practice
Browser Compatibility
| Engine | Supported | Notes |
|---|---|---|
| Chrome | Yes | — |
| Firefox | Yes | — |
| Safari | Yes | — |
| Edge | Yes | — |
| Node.js | Yes | — |