在JavaScript开发中,获取主域名是一项看似基础实则充满细节的技术任务。获取主域名的核心上文归纳在于:不能仅依赖简单的字符串截取或正则表达式,而必须基于“公共后缀列表”的规则进行解析,才能准确处理如 .com.cn、.co.uk 等复杂顶级域名,确保在跨子域名共享Cookie或进行权限校验时的安全性与准确性。

基础方法与常见误区
在探讨专业解决方案之前,我们需要先理解为什么常规方法会失效,许多开发者习惯使用 window.location.hostname 获取当前主机名,然后通过 split('.') 切割字符串,从 www.example.com 中提取 example.com。
这种简单的逻辑在处理标准的通用顶级域名(如 .com, .net, .org)时看似有效,但一旦遇到国家代码顶级域名或复杂的通用顶级域名,逻辑就会崩溃,对于 www.example.com.cn,简单的“取最后两部分”逻辑会得到 com.cn,这显然不是主域名,而是有效的公共后缀,同样,对于 example.co.uk,错误地提取会导致获取到 co.uk。
这种误区的根源在于混淆了“域名层级”与“公共后缀”的概念。 互联网域名体系中,并非所有的后缀都是单层的,为了解决这个问题,Mozilla 维护了一份公共后缀列表,它定义了哪些后缀是可以注册域名的“根”,任何专业的获取主域名方案,都必须建立在对这份列表的引用或规则匹配之上。
核心难点:公共后缀列表(PSL)的解析
要实现精准的主域名提取,必须理解公共后缀列表的工作机制,PSL 包含了所有互联网上被认可的公共后缀,包括 ICANN 管理的后缀和国家代码下的二级后缀(如 .uk 下的 .co.uk, .me.uk, .ac.uk)。
主域名的定义是:公共后缀的前一级域名。 在 blog.sina.com.cn 中,com.cn 是公共后缀,sina 才是主域名,在 news.bbc.co.uk 中,co.uk 是公共后缀,bbc 是主域名。
如果不引入 PSL 概念,单纯依靠正则表达式是无法穷举所有规则的。专业的前端开发应当放弃手写正则的“取巧”心理,转而使用成熟的算法或库来处理这一逻辑。
专业解决方案:基于 tldts 库的精准提取
在现代前端工程化体系中,最权威且性能最优的解决方案是使用 tldts(Top-Level Domain Toolkit)库,这是一个专门用于解析域名的轻量级库,它内置了 PSL 数据,并针对浏览器和 Node.js 环境进行了极致的性能优化。

使用 tldts 获取主域名的代码非常简洁且健壮:
import { parse } from 'tldts';
const url = 'https://sub.www.example.com.cn/path';
const result = parse(url);
// 核心属性:domain 即为主域名
console.log(result.domain); // 输出: example.com
console.log(result.subdomain); // 输出: sub.www
console.log(result.publicSuffix); // 输出: com.cn
这种方案的优势在于:
- 准确性: 它实时更新 PSL 规则,能够正确识别包括新通用顶级域名在内的所有域名结构。
- 性能:
tldts使用了 Fastest Smallest Polyfill 算法,解析速度极快,适合在高流量的前端页面中大规模使用。 - 全面性: 除了主域名,它还能提供子域名、ICANN 域名、私有域名等丰富信息,满足复杂的业务需求。
无依赖环境下的原生算法实现
在某些轻量级场景下,如果不允许引入第三方库,我们需要实现一个基于规则的“降级方案”,虽然无法完全覆盖 PSL 的所有边缘情况,但可以通过维护一个“常见公共后缀黑名单”来覆盖绝大多数业务场景。
核心逻辑是:先检查域名后缀是否在已知列表中,如果不在,则默认取最后两部分作为主域名;如果在,则向前多取一级。
function getMainDomain(hostname) {
// 常见的需要特殊处理的公共后缀列表(实际项目中应尽可能补全)
const commonPublicSuffixes = [
'com.cn', 'net.cn', 'org.cn', 'gov.cn', 'edu.cn',
'co.uk', 'me.uk', 'ac.uk', 'gov.uk',
'co.jp', 'ne.jp', 'ac.jp',
'com.hk', 'org.hk'
];
// 移除端口号
hostname = hostname.split(':')[0];
// 将域名按点分割
const parts = hostname.split('.');
// 检查后缀是否匹配特殊列表
// www.example.com.cn -> parts = [www, example, com, cn]
// 我们需要检查最后两部分 'com.cn' 是否在列表中
const suffix = parts.slice(-2).join('.');
if (parts.length > 2 && commonPublicSuffixes.includes(suffix)) {
// 如果是特殊后缀,主域名是倒数第三部分开始
return parts.slice(-3).join('.');
} else if (parts.length > 1) {
// 默认情况,取最后两部分
return parts.slice(-2).join('.');
} else {
// 只有 localhost 等情况
return hostname;
}
}
注意: 这种原生方法存在维护成本,因为 PSL 列表是动态变化的,如果业务涉及全球用户,强烈建议优先使用 tldts 库,仅在极度受限的环境下使用上述算法作为临时替代。
实战应用场景:跨子域名 Cookie 共享
获取主域名最典型的应用场景是设置跨子域名共享的 Cookie,在大型 Web 应用中,用户登录通常在 account.example.com,但业务访问在 www.example.com。
为了实现单点登录(SSO),我们需要将 Cookie 的 domain 属性设置为主域名。

// 错误的做法:直接设置当前域名
document.cookie = "sessionId=abc123; path=/; domain=www.example.com"; // 无法在 account 下读取
// 正确的做法:动态获取主域名并设置
const mainDomain = parse(window.location.href).domain;
document.cookie = `sessionId=abc123; path=/; domain=.${mainDomain}; Secure; HttpOnly`;
这里的关键点在于 domain 属性前的点号(.)。 设置 domain=.example.com 意味着该 Cookie 对 example.com 及其所有子域名(如 a.example.com, b.example.com)都可见,如果无法准确获取主域名,Cookie 要么无法跨域传递,要么会因为范围过大(如设置到了 .com.cn)而产生严重的安全风险。
安全与 SEO 优化建议
在处理域名逻辑时,安全性不容忽视。切勿在客户端直接获取主域名用于敏感的后端 API 鉴权跳转,因为 Host 头是可以被伪造的。 前端获取主域名主要用于 UI 展示、Cookie 设置或本地数据隔离,真正的权限校验必须在服务端进行。
从 SEO 角度来看,确保主域名的一致性有助于搜索引擎正确识别网站的品牌词,无论用户通过 www 还是非 www 访问,Canonical 标签都应指向统一的主域名规范,避免权重分散。
相关问答
Q1:在浏览器端使用 JS 获取主域名是否存在兼容性问题?
A1:使用 window.location 获取主机名在所有现代浏览器中都是完全兼容的,主要的兼容性挑战在于解析逻辑,如果使用 tldts 等现代库,它们通常支持 ES5+,能兼容 IE11 及以上浏览器(需配合 polyfill),如果使用原生正则方法,由于不同浏览器的 JS 引擎对正则的处理基本一致,因此也不存在兼容性问题,问题仅在于逻辑本身的准确性。
Q2:如何判断一个域名是主域名还是子域名?
A2:判断的核心在于对比“当前主机名”与“解析出的主域名”。window.location.hostname 的值不等于通过 PSL 规则解析出的 domain 值,且前者包含后者的字符串,那么当前访问的就是子域名,当前是 blog.example.com,解析主域名为 example.com,两者不等,说明是子域名;如果当前是 example.com,两者相等,说明是主域名访问。
能帮助您在项目中精准、高效地处理主域名获取问题,如果您在具体实施过程中遇到特殊的域名后缀难以解析,欢迎在评论区分享具体的 URL,我们可以共同探讨最佳的提取策略。

















