日常用的域名正则
域名是一串用点连起来的标签。每个标签的规则比大家想的更严,下面这条正则把它编码了进去。在拿用户敲的主机名去做任何危险操作之前,先用它做个健康检查。
^(?![0-9]+$)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.(?!-)[A-Za-z0-9-]{1,63}(?<!-))*$
它比那些"简单"版本长是有道理的:便宜版本放过的,恰恰就是会让解析器崩掉的字符串。
标签规则,逐条看
DNS 对每个标签和整串名字都有约束。下面是这条正则强制的东西:
- 每个标签是
[A-Za-z0-9-]{1,63}:字母、数字、连字符,一到 63 字符。 - 标签开头的
(?!-)禁止前导连字符,标签不能以-起头。 - 标签结尾的
(?<!-)禁止尾随连字符,标签不能以-收尾。 - 第一个标签上的
(?![0-9]+$)拒绝纯数字名字,那种会和 IP 地址撞车。 - 整串名字必须不超过 253 字符——这件事你还是得在代码里查,别全丢给正则。
合起来,它们拒绝 -example.com、bad..name.com 和 64 字符的标签,同时接受 sub.example.co.uk。
国际化域名得先转 punycode
用户可能敲 例子.测试 或 münchen.de。这些都是合法域名,但得先转成 punycode:xn--fsqu00a.xn--g6w251d 和 xn--mnchen-3ya.de。一条纯 ASCII 正则会把原样输入拒掉。正确管线是:发现非 ASCII,用平台的 punycode 编码器转(比如 Python 的 idna.encode),再去校验 ASCII 形式。别试图把 UTF-8 塞进正则,编码器已经解决过的边缘情况你会漏。
正则是永远证明不了的事
核心论点:正则可以确认这串东西"长得像域名"。它确认不了这个域名有没有注册、能不能解析、是不是你的。 this-domain-does-not-exist-anywhere.example 完美匹配正则,却解析不到任何东西。所以一条通过的正则只是必要,却一点也算不上"存在"的证明。当存在性真要紧时,配一次 DNS 查询(getaddrinfo、一次 SOA 查询,或查注册商)。
合法与非法,一眼对照
| 输入 | 期望 | 原因 |
|---|---|---|
example.com | 接受 | 经典两段名 |
sub.example.co.uk | 接受 | 多段完全没问题 |
-example.com | 拒绝 | 标签以连字符开头 |
bad..name.com | 拒绝 | 点之间是个空标签 |
一长串超过 63 的标签 | 超长则拒绝 | 标签长度上限 |
把真域名写残的常见错误
| 错误 | 后果 | 改法 |
|---|---|---|
| 放行下划线 | SRV 记录才用,主机名不该有 | 除非你真指 SRV,否则别放 _ |
| 放行连续点 | a..b 不是合法名字 | 每个点之间都得有标签 |
| 忽略末尾点 | example.com. 表示根域,是合法的 | 可有可无地放一个结尾点 |
| 写死顶级域白名单 | 新顶级域不断冲破你的列表 | 校验长度和形状,别校验成员资格 |
强制顶级域的更严版本
如果你只在乎经典公网域名、想一上来就拒掉 IP 字面量和 IDN,就强制末尾是字母标签:
^(?![0-9]+$)(?!-)[A-Za-z0-9-]{1,63}(?<!-)(\.(?!-)[A-Za-z0-9-]{1,63}(?<!-))*\.[A-Za-z]{2,}$
这更严,也拒绝更多合法输入,所以只在你的下游系统真的吃不进别的任何东西时才用。
正则之外:验证存在性
当域名真得能用时,跨过正则这一步:
- 校验前先把 IDN 转成 punycode。
- 跑一次 DNS 解析或 SOA 查询,确认已注册。
- 注册场景就往那个域名的邮箱发验证链接。
- 总长度 253 在代码里强制,正则的长度算术太难看。
正则是门房,DNS 是保安。各有各的活,而且只有其中一个知道里面到底是谁。
怎么测你的域名正则
把表中的样例丢给你的正则,再加一个 253 字符的名字和一个 punycode 名字。如果你的正则会放过 a..b 或以连字符开头的标签,就把锚收紧。如果它拒绝了 example.com. 而你需要根域支持,就放行最后的那个点。记住:匹配不等于拥有。
子域、层数,与 253 上限
真实的名字可以很长、嵌套很深。一条只查每段的正则会漏掉整串 253 字符的上限,也漏掉多数解析器实际能处理的层数。 a.b.c.d.e.f.g.h.example.com 形状上合法,但可能超标,也可能撞上解析器的实际上限。正则放行后,在代码里强制 253 字符总长,并想清楚你接不接任意层数。注册表单里限制一下子域层级是合情合理的;DNS 工具就放开语法允许的范围,把最终裁判权交给查询——由它来定这个名字到底活不活。