URL 解析器

使用 WHATWG URL 标准把任意 URL 拆解为协议、主机、端口、路径、查询和片段,并把查询参数解码成可读表格。

把一条 URL 拆成浏览器看到的每个组成部分。 粘贴 URL,工具会用与浏览器完全相同的 WHATWG 解析器提取各部分,因此结果就是这条 URL 实际会被如何解释。查询参数会解码后列成表格。

查询参数

URL 的解剖结构

URL 的骨架

每条绝对 URL 都遵循同一个骨架:

scheme://user:password@host:port/path?query#fragment

大多数 URL 只用到其中几段,但语法上始终允许全部存在。读懂一条 URL,本质上就是知道每一段在哪里结束、下一段从哪里开始 —— 这由 ://@:/?# 这几个分隔符决定。

逐段拆解

协议(scheme) 指明如何获取资源:https:mailto:ftp:。注意 WHATWG 解析器返回的值包含结尾冒号,很多人拿它去和字符串 "https" 比较时会踩坑。

用户名与密码 属于已废弃的 userinfo 段。浏览器仍会解析,但会从请求中剥离并常常给出警告,因为把凭据写进 URL 会让它泄漏到历史记录、日志和 referrer 头里。如果你在一条不是自己写的 URL 里看到它们,应当视为钓鱼信号。

主机名(hostname) 只是域名或 IP。主机(host) 在存在非默认端口时会带上端口,这也是这两个字段经常看起来一样的原因。

端口 在使用协议默认值时为空 —— https 是 443,http 是 80。解析器会做归一化:example.com:443/ 报告的端口为空,因为 443 没有提供任何额外信息。

路径 是从第一个 / 到查询之前的全部内容。对 http(s) URL 来说,它是唯一永远不为空的部分;只有源的 URL 会得到 / 作为路径。

查询字符串? 开始,承载参数。片段# 开始,是唯一永远不会发送给服务器的部分 —— 它完全由客户端处理,这正是早期单页应用用它做路由的原因,也是把访问令牌放在片段里能避开服务端日志的原因。

源(origin) 是协议、主机、端口这个与安全相关的三元组。只有三者全部相同,两条 URL 才同源,而同源策略和 CORS 判定正是基于它。

解析器为什么这么严格

WHATWG URL 解析器只接受绝对 URL。example.com/page 没有协议,无法单独解析 —— 解析器无从判断你指的是 example.com/page 还是一个相对路径。真实代码会拿基准 URL 去解析相对引用;本工具刻意要求绝对形式,这样你看到的结果不会有歧义。

你会注意到的归一化

解析器不会原样返回输入字符串。它会把协议和主机转为小写、消解路径中的 ... 段、去掉默认端口,并把国际化域名转成 Punycode 形式,所以 münchen.de 报告的主机名是 xn--mnchen-3ya.de。这是特性而非缺陷:它展示的是网络栈实际对待这条 URL 的样子,而不是你敲进去的样子。

重复的参数

查询字符串完全可以重复同一个键,比如 ?tag=a&tag=b。没有任何规范规定这该如何解释 —— 有的框架取第一个值,有的取最后一个,有的收集成数组。这里的表格按顺序列出每一次出现,让你看到实际发送的内容,而不是被合并后的视图。

开源说明:使用浏览器内置的 WHATWG URLURLSearchParams API 实现,未使用第三方库。

常见问题

为什么端口字段是空的?
因为这条 URL 用的是该协议的默认端口。WHATWG 解析器会省略 https 的 443 和 http 的 80,因为写出来不提供额外信息,反而会破坏源的比较。
协议为什么带一个冒号?
这是 URL 标准的定义。protocol 属性的值是 'https:' 而不是 'https'。如果你要和裸协议名比较,记得处理这个结尾冒号。
host 和 hostname 有什么区别?
hostname 只是域名本身,host 在存在端口时会带上端口。对 https://example.com:8443/ 来说,hostname 是 example.com,host 是 example.com:8443。
为什么不能解析相对 URL?
/about 或 ../img.png 这类相对引用只有相对某个基准 URL 才有意义。解析器要求带协议的绝对 URL,这样结果才不会有歧义。
片段会发送给服务器吗?
不会。# 之后的内容从不出现在 HTTP 请求里,完全由浏览器处理。这也是 OAuth 隐式流曾用它来避免令牌进入服务端日志的原因。
我的中文/Unicode 域名为什么变样了?
国际化域名会被转换成 Punycode,也就是 DNS 实际使用的 ASCII 形式。münchen.de 会变成 xn--mnchen-3ya.de。浏览器在发起请求前做的正是同样的转换。