正则表达式匹配域名,核心在于理解域名层级结构(标签、点号、顶级域),先写一个能覆盖常见场景的基础公式:^(?!-)[A-Za-z0-9-]{1,63}(?<!-)(.[A-Za-z0-9-]{1,63})+$,再根据校验还是提取、是否允许子域名、是否限制顶级域来微调。这篇文章会拆解每一步,给出可直接用的代码片段,并回答几个让你头疼的边界问题。
域名长什么样,正则才不写错
写正则前,先和域名“对齐颗粒度”,一个完整域名由标签(label)和点号(.)组成,www.baidu.com 有三个标签:www、baidu、com,行业共识认为,域名规则遵循RFC 1035和RFC 1123,核心约束有三条:
- 每个标签长度1到63个字符,整个域名最长253个字符(含点)。
- 只用字母(a-z)、数字(0-9)和连字符(-),但连字符不能出现在标签开头或结尾。
- 标签之间用点号分隔,顶级域(如
.com、.cn、.org)可以包含字母或国际化域名(punycode形式如xn--fiqs8s)。
理解了这三条,正则就不会写成“一坨”,很多人拿一个简单的 ^[a-zA-Z0-9.-]+$ 去糊弄,结果把 -abc.com、abc..com 这种非法域名也放进来了,针对域名正则表达式匹配后校验不通过的问题,先检查标签是否以连字符开头。
最常用的域名匹配正则:校验与提取,分两种写法
纯校验:只要域名合法,不要子域名拆分
当你需要判断一个字符串“是不是合法域名”时,用下面这个(兼容最长253字符):
^(?=.{1,253}$)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(.(?!-)[A-Za-z0-9-]{1,63}(?<!-))+$
拆解关键点:
(?=.{1,253}$)限制总长度。[A-Za-z0-9-]{1,63}匹配每个标签的字符集和长度。(?<!-)是负向后顾,确保连字符不出现在标签结尾。(.(?!-)[A-Za-z0-9-]{1,63}(?<!-))+要求至少有一个点,且点后标签同样不能以连字符开头。
这个正则会拒绝 -foo.com、foo-.com、foo..com,但接受 foo.com、a-b.com、sub.domain.example.org。注意:它不会校验顶级域是否真实存在,abc.xyz123 也会通过,因为 .xyz123 在语法上合法,要限制真实顶级域,需要把顶级域列表写进正则,但列表太长且会更新,不建议硬编码。
提取场景:从文本中捞出所有域名
如果要从一篇文章、日志或配置里提取域名,推荐使用更宽泛的表达式,因为文本里常有 http:// 前缀、路径、端口号等干扰,先提取“看起来像域名”的部分,再用上面的严格正则二次过滤,直接提取用这个:
b(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?.)+[a-zA-Z]{2,63}b
这个模式允许标签中间有连字符,但开头和结尾必须是字母数字,同时顶级域至少2位字母,用它匹配 访问 https://www.example.com/path?q=1 试试,能得到 www.example.com,但注意它会漏掉国际化域名(如

例子.中国 的punycode形式),因为顶级域是 xn-- 开头。
子域名如何匹配:要不要把 www 单独处理
场景:只想匹配主域名,排除子域名
很多情况下,你需要把 www.example.com 和 example.com 视为同一域名,统计主域时,正则就不能简单地“从头匹配到顶级域”,有两种常用做法:
- 剥掉子域名再校验:先找到最后一个点,取最后两个标签作为主域,这个用正则懒量词可做:
([a-zA-Z0-9-]+.)+([a-zA-Z]{2,})$,然后用第二个捕获组提取主域。 - 精确排除特定前缀:比如不要
www,用^(?!www.)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(.[A-Za-z0-9-]{1,63}(?<!-))+$,这样www.foo.com不匹配,foo.com匹配,但wwwx.foo.com会匹配,因为它不是严格的www.。
这里引出一个问题:域名正则表达式测试工具选哪个好?业内常用的有 regex101.com 和正则可视化工具,可以实时看分组匹配内容,我在调试时习惯在 regex101 上切换“Greedy”和“Lazy”模式,观察捕获组的变化。
场景:允许任意层级子域名
如果业务上不限制层数,用 ^(?!-)[A-Za-z0-9-]{1,63}(?<!-)(.[A-Za-z0-9-]{1,63}(?<!-))+$ 就够,如果限制最多3层(如 a.b.example.com),把 改成 {1,3}:
^(?!-)[A-Za-z0-9-]{1,63}(?<!-)(.[A-Za-z0-9-]{1,63}(?<!-)){1,3}$
这个正则匹配 example.com、b.example.com、a.b.example.com,但拒绝 a.b.c.example.com,实际部署时,很多CDN和防火墙规则就是用这个思路限制子域名层级,防止恶意构造超长域名绕过黑白名单。
端口号和协议:域名匹配要不要带上它们
有人问“正则表达式匹配域名带端口怎么写”,这是另一个高频场景,单独校验域名时,端口不属于域名部分,但提取URL里的域名时需要把端口、路径、协议剥掉,推荐做法:先用 ^(?:https?://)?([^/s?#]+) 提取主机部分(可能含端口),再用下面这个正则分离域名和端口:
^(?<domain>(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?.)+[a-zA-Z]{2,63})(?::(?<port>d{1,5}))?$
命名分组 domain 和 port 方便后续处理,这个正则会接受 example.com:8080,但不会把 8080 当作域名的一部分,注意端口号范围实际是0-65535,d{1,5} 只限制位数,不限制数值范围,如果需要严格校验,把数值判断放在代码里做,别用正则硬刚。
实战:从日志和配置中批量匹配域名
假设你手头有一份nginx访问日志,需要统计访问了哪些外部域名,用 grep -E 加上上面的提取正则:
grep -Eo '[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+' access.log | sort | uniq -c | sort -rn
这条命令的思路是:-E 启用扩展正则,-o 只输出匹配部分,后面跟管道去重计数,如果日志里有大量IP地址(如 168.1.1),上面的正则不会匹配,因为顶级域必须是字母,IP不会误判,这算是个额外福利。

在Python里,可以用 re.findall(r'b(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?.)+[a-zA-Z]{2,63}b', text),返回所有候选域名,但一定要对结果做二次校验,因为文本里可能混有 example.com.(结尾带点),或者 a..b 这种异常字符串,二次校验直接用第一节的严格正则即可。
常见陷阱:连字符、国际化域名和换行符
连字符的坑
-a.com 和 a-.com 都是非法域名,但很多正则没排除,上面所有表达式都用了 和 (?<!-) 前后断言,注意: 是负向前瞻,表示“后面不能是连字符”;(?<!-) 是负向后顾,表示“前面不能是连字符”,在JavaScript、Python、Java里都支持,但有些老工具(如 grep 的基本正则)不支持“向后顾”,这时需要改用字符组排除:[a-zA-Z0-9]{1,62}[a-zA-Z0-9],但这样会强制标签至少2位,无法匹配单字符标签(如 a.com),替代方案是拆开写:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]),表示要么单字符,要么首尾非连字符的多字符。
国际化域名(IDN)怎么办
中文域名如 百度.中国,在DNS和URL里实际存储为 xn--wxtr44c.xn--fiqs8s(punycode),上面所有正则都能匹配上述punycode形式,因为 xn-- 前缀包含连字符,但连字符不在开头,标签为 xn--wxtr44c,符合规则,如果你要直接匹配中文域名原文,需要引入非ASCII范围,^[u4e00-u9fa5a-zA-Z0-9-]+(.[u4e00-u9fa5a-zA-Z0-9-]+)+$,但实际场景中,建议统一先转punycode再匹配,避免编码不一致导致漏报。
换行符和URL截断
默认情况下,正则的 ^ 和 匹配字符串开头和结尾,而不是行的开头结尾,如果从多行文本提取,记得用 re.MULTILINE 标志,或者用 b 边界,比如从HTML源码提取域名,域名后面可能紧跟着 < 或 ,b 能正确识别边界,但如果域名以数字结尾,后面又跟数字,边界就会失效,保险做法是匹配后取前后字符验证。
针对不同编程语言的微调建议
- JavaScript:不支持“向后顾”在旧版中,但新版(ES2018+)支持,如果你要兼容旧浏览器,用上面的“首尾非连字符”替代方案。
b在JavaScript里对中文不生效,只对ASCII有效。 - Python:天然支持 和
(?<!-),但re模块的 在字符串末尾有换行时也会匹配,建议用Z代替 确保绝对结尾。 - Java:默认不区分ASCII大小写,需要显式加
(?i),但域名本身不区分大小写,这点没问题。 - MySQL:正则函数
REGEXP不支持向后顾,且引擎默认大小写不敏感,可以用REGEXP '^[a-z0-9-]+(\.[a-z0-9-]+)+$',但连字符边界问题最好在应用层处理。
行业内有个常见误区:用同一个正则同时校验域名和URL,这会导致复杂度和误判率同时上升,更稳的架构是:先用URL正则拆出host,再用域名字符串正则校验,模块化比一个“万能正则”更可靠。
域名正则表达式的性能优化

如果要对百万级URL做实时过滤,正则性能不容忽视,业内专家指出,回溯是正则卡顿的主因,下面的写法容易导致灾难性回溯:
^([a-zA-Z0-9-]+.)+[a-zA-Z]{2,}$
因为 和 都可以重复,嵌套量词会导致大量回溯,解决办法是原子组或占有量词,在支持原子组的语言(如Python的 regex 模块或Java)中写:
^(?>[a-zA-Z0-9-]+.)+[a-zA-Z]{2,}$
或者用非贪婪和边界锚点减少回溯。编译正则并复用,不要在循环里重复 regex.compile,这比优化表达式本身更见效,对于海量文本,先用简单字符串查找(如找 ),再对小范围跑正则,能提升数量级的速度。
结合业务场景:到底该用哪种正则
我整理了一个选型表格,方便你直接抄作业:
| 业务场景 | 推荐正则模式 | 核心限制 |
|---|---|---|
| 表单校验“请输入域名” | 严格校验(含长度和连字符断言) | 不校验顶级域真实性 |
| 从文本提取域名 | 宽松提取 + 二次严格过滤 | 可能漏掉IDN原文 |
| 统计主域忽略子域 | 提取最后两个标签 | 需要代码逻辑,非纯正则 |
| URL中分离域名和端口 | 命名分组 + 端口可选 | 端口范围需代码判断 |
| 黑白名单匹配子域名 | 固定子域名层级 {1,N} |
N要按业务定 |
选择依据很简单:正则只是第一步,永远不要指望一条正则解决所有业务逻辑,比如你要过滤赌博网站域名,光靠正则语法校验没用,需要维护一份真实域名黑名单并做模糊匹配。
常见问题快问快答
正则表达式匹配域名和网址有什么区别
网址(URL)包含协议、路径、参数等,而域名只是其中的主机部分,匹配完整网址需要用 ^(?:https?://)?(?:[^/s]+?)(?:/.)?$,但匹配域名只关心点分标签结构,实际开发中,你完全可以用一个正则提取URL的host,再用另一个校验域名,两段式更清晰。
如何让域名正则同时匹配IP和域名
IP地址的四段数字和域名的标签结构相似,但顶级域是数字,所以可以用 ^((d{1,3}.){3}d{1,3}|(?:[a-zA-Z0-9-]+.)+[a-zA-Z]{2,})$ 做二选一,但要注意,IP的每段数值范围是0-255,d{1,3} 会匹配 999.999.999 这种非法IP,需要额外数值校验。
域名正则中 ^ 和 什么时候必须用
当你做全字符串校验时,必须用它们,否则 abc.com.evil.com 也会匹配出 abc.com 的子串,如果你用 re.findall 提取,则不需要 ^ 和 ,而要用 b 边界,记住一句口诀:校验用头尾锚,提取用边界锚。
最后说回本质:正则表达式匹配域名,不是背一条公式就万事大吉,而是理解域名的边界规则、标签长度、连字符限制和匹配模式,把上文的正则存进你的代码片段库,遇到具体场景时对着表格微调,远比临时搜来的“万能正则”可靠。
图片来源于AI模型,如侵权请联系管理员。作者:酷小编,如若转载,请注明出处:https://www.kufanyun.com/ask/763083.html

