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 | — |
Work with this pattern
Take this expression further — test it against your own data, adapt it in the builder, or read up on the syntax behind it.
Regex Tester
Test a regular expression against sample text and see every match highlighted live.
Regex Generator
Build a pattern by clicking blocks — no syntax memorisation required.
Regular Expressions Guide
What regex is, how the syntax works, and the mistakes that trip people up.
Regex Cheat Sheet
Every token, quantifier, and assertion on one printable page.
Python Regex Guide
The re module end to end — search, match, findall, groups, and substitution.
Regex Pattern Library
100+ ready-to-use patterns for email, URLs, dates, IPs, and more.