URL(Uniform Resource Locator,统一资源定位符)就是"网上那个东西在哪、怎么取"的一串写法。它由六段拼成:协议名 / 主机名 / 端口 / 路径 / 查询串 / 片段。它的精妙之处不在"长什么样",而在于每一段归不同的人管——协议段浏览器用掉,主机段交给 DNS 换成 IP(3.21),端口段交给 TCP 去连(3.17),路径与查询串装进请求行发给 Web 服务器软件,而最后那段 # 锚点连服务器都收不到。所以你改网址的哪一段、有没有用,完全取决于改的是哪一段。
4.1 里已经出现过一条完整 URL,当时只是顺手指给我们看"为什么 #top 服务器收不到"。
本节把它扶正做主角,干三件事:① 逐段拆开,说清每段是什么、能不能省;② 说清每段归谁管——这是理解"改了网址为什么没反应"的钥匙;③ 补上它最反直觉的一层:你写的和真正发出去的往往不是同一串字符(百分号编码)。
一、URL 是什么
URL = Uniform Resource Locator = 统一资源定位符。 三个词逐个拆:
| 词 | 意思 | 说人话 |
|---|---|---|
| Uniform(统一) | 一套统一的写法 | 网页、图片、视频、接口……所有东西都用同一套格式描述,浏览器不必为每种东西学一套语法 |
| Resource(资源) | 被取的东西 | 严格说不只是"文件",也可能是"程序现算出来的一段内容"(4.1 的动态内容) |
| Locator(定位符) | 按位置去指 | 它说的是"在哪",不是"是什么"。所以同一个 URL 昨天给的是这篇文、今天可能换了一版 |
规范里的正式名字其实是 URI(Uniform Resource Identifier,统一资源标识符)。它下面分两支:
- URL:按位置指东西——"去那个地址拿"
- URN:按名字指东西——"这本书的 ISBN 是 978-7-…",不管它放在哪个书架
理想世界应该用 URN(东西搬家了链接也不用改),但现实中几乎全是 URL。所以日常说的"网址"、课本里的"URL"、规范里的"URI",指的多半是同一件东西——知道它们是"一家人"就够了,不必在措辞上纠结。
域名只回答一个问题:"哪台主机"(收 3.21)。
URL 要多回答两个:"用什么协议取" 和 "取这台主机上的哪一个资源"。
所以 www.example.com 是域名,而 https://www.example.com/about.html 才是 URL——域名只是 URL 里的第二段。
二、六段解剖
拿一条最全的 URL 为例:
https://www.example.com:443/path/page.html?q=1#top
| # | 段名 | 这一段 | 作用 | 能省吗 |
|---|---|---|---|---|
| 1 | 协议名 scheme | https:// |
用哪种协议说话,同时定下默认端口 | 能(浏览器补,但有时补错) |
| 2 | 主机名 host | www.example.com |
哪台主机,交给 DNS 换成 IP | 不能(可写 IP 代替) |
| 3 | 端口号 port | :443 |
连那台主机的哪一个程序 | 能(省了就用协议的默认端口) |
| 4 | 路径 path | /path/page.html |
那台主机上的哪个资源 | 能(省了就是 /) |
| 5 | 查询串 query | ?q=1 |
取这个资源时附加的参数 | 能 |
| 6 | 片段 fragment | #top |
跳到页面里的哪一小节 | 能 |
前四段合起来读像一句话:「用 https 去 example.com 这台机器的 443 号门口,拿 /path/page.html 这个文件」。
后两段是给这次"取"附加的修饰:? 是给服务器的备注,# 是给自己看的位置。
几个容易忽略的细节:
- 协议段决定默认端口:
http://默认 80,https://默认 443(收 3.17 的已知端口)。所以 URL 里写不写端口,往往指的是同一件事。 - 协议还有一串亲戚:
ftp://(传文件)、file://(本机磁盘,4.2 双击打开走的就是它)、mailto:(写邮件)、data:(内容直接写在 URL 里)。file://和http://是两条完全不同的路——一条根本不出网卡。 https比http多的那个 s = secure:http 是明文,路上谁都能看;https 在外面套了一层加密。所以它俩不是"版本高低",是"有没有穿衣服"。- 路径段不一定真有一个文件:静态网站里
/about.html对应磁盘上的about.html;动态网站里/user/123可能压根没有这个文件,是程序现场拼出来的(4.1 的静态 vs 动态)。别把 URL 的路径当成本地文件路径。
三、每一段归谁管
这是本节最值钱的一条:URL 不是一句口令,是一张分工单。
- 协议名 → 浏览器自己用掉。它决定接下来用哪套协议、连哪个默认端口,不会原样发给服务器。
- 主机名 → 交给 DNS 解析模块(收 3.22)换成 IP 地址。这一步是"每次都要做"的(要么命中缓存、要么冷查询)。
- 端口号 → 交给 TCP(收 3.17)。它决定跟服务器的哪个程序对话——80/443 后面站着的正是 4.1 说的"门房"(Web 服务器软件)。
- 路径 + 查询串 → 装进请求行,发给 Web 服务器软件。门房接住这两段,去磁盘找文件、或者交给后面的程序处理。
- 片段 → 浏览器自己留着。一个字节都不发出去。
① 在地址栏里改 # 后面那截,页面不会重新加载——它压根不出门,浏览器只是自己滚了一下屏幕。
② 同一个页面链接发给人,对方打开后"没跳到那一节"——因为 # 那段到了对方浏览器才生效,中间任何跳转、重定向都可能把它丢掉。
③ 改了路径或查询串,页面立刻变——因为这两段是真的发给服务器的,服务器照着重新取了一遍。
四、能省掉的那些段
URL 有一堆"省略写法",浏览器会替你补全。这正是"同一个页面有好多条网址"的来源:
| 你写的 | 浏览器实际理解的 | 省掉了什么 |
|---|---|---|
example.com |
http://example.com/ 或 https://example.com/ |
协议 + 路径 |
http://example.com/ |
http://example.com:80/index.html |
端口 + 默认文档名 |
https://example.com:443/ |
https://example.com/ |
端口(地址栏干脆把它隐藏了) |
example.com/a |
http://example.com/a(路径区分大小写) |
协议 + 端口 |
- 协议最好写全。让浏览器替你猜,它可能补成
http://(明文),也可能因为记着上次的https而补对——猜错就是"该加密的没加密"。 index.html不用写。路径以/结尾时,服务器会拿出它配置的默认文档(通常是index.html,收 4.2 里"index 是首页惯例")。- 默认端口不用写。但非默认端口必须写——比如本地那个
python -m http.server 8000起的服务站,URL 里不写:8000就永远连不上(收 4.1)。
五、绝对 URL 与相对 URL
写链接(<a href>、<img src>、<link href>)时,地址可以有两种写法:
| 类型 | 长什么样 | 相对谁算 | 用在哪 |
|---|---|---|---|
| 绝对 URL | https://example.com/about.html |
不相对谁,自带全套 | 跨站链接、邮件、文档里引用 |
| 相对 URL | about.html / images/a.png / ../style.css |
相对当前页面所在地址 | 站内自己的图片、样式、链接 |
| 根相对 | /images/a.png(以 / 开头) |
相对这个网站的根 | 站内跨目录引用 |
两个实在好处:
① 换域名不用改文件。整站从 test.example.com 搬到 www.example.com,绝对链接全得改,相对链接一个不用动。
② 本地预览和线上一致。在本地用 file:// 或 python -m http.server 打开,相对路径照样能戳到图;绝对地址一到本地就废了。
代价是容易算错层级。4.2 说的"图片破图 404",九成就是相对路径算错——浏览器是拿"当前页面的地址"当基准去拼的,不是拿"文件在硬盘的哪里"。路径里多一层目录、少一个 ../,结果就完全不同。
一条可以背下来的拼法:相对地址 = 把当前页面地址的最后一段(文件名)删掉,再把相对地址接上去。
六、你写的 ≠ 发出去的:百分号编码
URL 里有一批字符是"语法字符",它们本身有结构含义:
/ ? # & = + 空格
如果这些字符出现在数据内容里(比如搜索词里真有空格、参数值里真有 &),直接放进去就会把 URL 的结构搅乱:服务器会把你内容里的 & 当成"下一个参数的开始"。
于是有了百分号编码(percent-encoding):把"不安全、不能直接放"的字符,按字节写成 % + 两位十六进制。
| 原来 | 编码后 | 说明 |
|---|---|---|
| 空格 | %20 |
URL 里最经典的转义 |
& |
%26 |
不然会被当成参数分隔符 |
? |
%3F |
不然会被当成查询串起点 |
# |
%23 |
不然会被当成锚点起点 |
中 |
%E4%B8%AD |
先按 UTF-8 变成三个字节 E4 B8 AD,再逐字节加 % |
注意最后一行:一个中文字 = 9 个字符。所以含中文的地址会"凭空长胖三倍"——这也解释了一条长中文搜索 URL 为什么那么长。
在查询串里,+ 常常被当成空格(这是老式表单提交留下的惯例);但在路径里,+ 就是加号。
所以最稳的做法:空格一律用 %20,别赌 +。
谁来做编码?
- 浏览器发请求前会自动编——你在地址栏敲中文,它出门前替你转好了,你甚至察觉不到。
- 但你自己拼 URL 时必须自己编。脚本、程序里拼接
?q=+ 用户输入的值,不编码就会出错——这既是 bug,也是安全漏洞(注入)的常见入口。前端里的encodeURIComponent()就是干这个的。
你输入 中文.com 也能访问,但它靠的是另一套叫 Punycode 的转换:中文.com 实际被转成 xn--fiq228c.com 这样的形式再去查 DNS。
看到以 xn-- 开头的域名,那就是一条中文(或日文、韩文)域名——知道这个就够,不必深挖。
七、发出去的时候,六段各去哪了
把这一节和 4.1 的"门房"接上。当你敲下回车、请求真正离开你的电脑时,URL 的六段被"拆散分家"了:
| URL 里的段 | 最后去了哪 |
|---|---|
| 协议名 | 不发送。浏览器自己用它决定协议与默认端口 |
| 主机名 | 不按原样发送给网页程序。先被 DNS 换成 IP 用于连接;再作为 HTTP 的 Host 头带上(HTTP/1.1 必须带,否则服务器不知道你要哪个站——收 4.1"一台机器托管多个站靠域名区分") |
| 端口号 | 不发送。在建立 TCP 连接时就"用掉"了(收 3.17) |
| 路径 + 查询串 | 原样装进请求行,这才是服务器真正读到的东西:GET /path/page.html?q=1 HTTP/1.1 |
| 片段 | 不发送。留在浏览器手里 |
服务器永远只看到「路径 + 查询串」,其它几段都在路上被各层消化掉了。
这正好接上第四章的下一站:网页是怎么被"要"回来的——那条请求行本身,就是 HTTP 协议的开场白。
八、URL 的四个脾气(限制与坑)
① 长度有上限。 规范没写死,但浏览器、服务器、中间代理各有自己的长度限制(几千字符是常见量级)。所以别拿 URL 传大块数据——搜索关键词、分页参数可以,一整篇文档就不行。
② 同一个资源可能有好几条 URL。 example.com / www.example.com / example.com:80 / example.com/index.html 很可能指向同一个页面;再叠上 ?utm_source=… 这类跟踪参数,同一个页面能有几十条不同地址。后果很具体:浏览器缓存算成好几份(该命中的没命中)、统计后台把一页算成十几页。所以正规站点要做 URL 规范化:把多写法 301 跳到唯一那条。
③ 路径不是文件路径,但确实有脾气的细节。
- 域名不区分大小写(Example.com = example.com),路径区分大小写(/About 和 /about 是两个地址,在 Linux 服务器上尤其如此)。
- 结尾斜杠常被区别对待:/about 和 /about/ 在服务器看来可能不是一回事,常常一个会 301 跳到另一个(收 4.1 的状态码)。
——这也是"本地能打开、上传服务器就 404"的经典原因之一。
④ URL 不能当安全边界。 路径里出现了 ../(/img/../../etc/passwd 这种写法),如果是"拿到路径就直接拼到磁盘上打开"的服务器,就会被一步步走出网站目录。服务器必须校验路径、限制在网站根目录内——这是 Web 安全里最古老的漏洞之一。"URL 是用户能随便改的输入",这条意识要先有。
九、动手 Demo(Demo41)
目标:把你每天敲的那串字符,从"眼熟"变成"看得懂、拆得动"。
第 1 步 · 看浏览器替你补全了什么
地址栏只敲 example.com 回车,再点一下地址栏看完整形式:前面被补上了协议,端口看不见(被隐藏了)。换成 http://example.com:80/,看它又怎么简化。
第 2 步 · 在网络面板里看"被拆开的查询串"
按 F12 → 网络 → 刷新 → 随便点一条请求 → 右侧「标头」里找 请求 URL,再找 查询字符串参数——浏览器已经把它翻译成一张"键 = 值"的表了。以后看参数别肉眼数 &,直接看这张表。
第 3 步 · 控制台两行,亲手编一次码
encodeURIComponent('中文 空格&') // → "%E4%B8%AD%E6%96%87%20%E7%A9%BA%E6%A0%BC%26"
decodeURIComponent('%E4%B8%AD') // → "中"
对照图二:中 → %E4%B8%AD,空格 → %20,& → %26。编码和解码就是一对逆运算。
第 4 步 · 在地址栏里做一次"真编码"
随便找个搜索引擎,敲带空格和中文的关键词搜一次,然后把地址栏里的地址整条复制出来——你会看到 %E4%B8%AD%E6%96%87%20 这种东西。这就是"你写的"和"发出去的"的差距。
第 5 步 · 验证"片段真的不发给服务器"(本节最关键的实验)
找一个长文档页(任何带目录/锚点的说明书页都行),点一下目录里的锚点跳转:
- 地址栏的 # 后面变了
- 网络面板里一条新请求都没有
这就铁证了:# 后面那段服务器根本收不到。
第 6 步 · 用 curl -v 看请求行,六段"分家"的现场
curl -v "https://example.com/search?q=%E4%B8%AD"
在输出里找那一行 > GET /search?q=%E4%B8%AD HTTP/1.1——里面没有 https://、没有主机名、没有片段,只剩路径与查询串。主机名则出现在下面的 > Host: example.com 一行。
这一屏,就是第七节那张表的实物。
第 7 步 · 亲手撞一次相对路径的坑 用 4.2 那套起个本地服务器:
mkdir -p sub && echo '<img src="img/a.png">' > sub/index.html
python -m http.server 8000
然后分别访问 http://localhost:8000/sub/ 和 http://localhost:8000/sub,看图片能不能出来(服务器会替你 301,浏览器再次拼路径时的基准就变了)。这一下能让你彻底明白"相对路径是相对谁"。
十、30 秒实验
- 30 秒:把一条 URL 里的
#改成?,观察行为完全不同——#那版不发请求,?那版会重新发一次请求。同一条网址,一个字符之差,去的地方完全不同。 - 30 秒:在地址栏手工输入
http://example.com:80/和https://example.com:443/,看地址栏怎么把默认端口悄悄收起来。 - 30 秒:故意把路径里一个字母改成大写(
/About→/about),看是否 404。验证路径区分大小写。
十一、常见误区
- "URL 就是域名。" —— 域名只是六段里的第二段。URL 还包含协议、端口、路径、查询串、片段。
- "URL 里的路径就是服务器上的文件路径。" —— 静态网站像,动态网站只是"路由",
/user/123背后可能压根没有这个文件(4.1 静态 vs 动态)。 - "改了
#后面那截会重新加载页面。" —— 不会。它只在浏览器内部生效,一个字节都不出门。 - "http 和 https 只是多了个字母。" —— 一个是明文,一个是加了加密。不是版本高低,是"有没有穿衣服"。
- "我在地址栏敲的中文就是原样发给服务器的。" —— 出门前被换成
%E4%B8%AD这类形式,中文会"长胖"到三倍。 - "URL 里不写端口就是没有端口。" —— 是默认端口:http 80、https 443(收 3.17)。
- "末尾那个斜杠有没有都一样。" —— 服务器常常区别对待,一个会 301 跳到另一个。这是"本地能开、上线 404"的常见原因。
- "
+就是空格。" —— 只在查询串里这么理解,路径里+就是加号。稳妥做法是一律用%20。 - "一个资源就一个 URL。" —— 一个页面往往有好几条等价 URL(www 与否、末尾斜杠、跟踪参数),缓存和统计都会因此对不上账。
- "URL 是安全的,服务器直接拿它去找文件就行。" —— URL 是用户能随手改的输入。路径里的
../这类东西,服务器必须防。
十二、动手自测
- URL 三个字母分别是什么?它和"域名"的关系是什么?
- URL 的六段分别是什么?哪一段"服务器根本收不到"?
- 请求真正发出时,路径和查询串装在哪里?主机名又去哪了?
https://和http://的默认端口各是多少?"不写端口"是什么意思?- 为什么说"路径不一定对应磁盘上的文件"?
https://example.com/这条地址里,实际被省掉了哪两段?- 绝对 URL 和相对 URL 怎么区分?站内链接为什么优先用相对地址?
- 空格在 URL 里写成什么?
中一个字为什么变成 9 个字符? - 为什么"改了
#后面的内容,页面不重新加载"?用本节的分工来解释。 - 域名和路径,哪个区分大小写?结尾斜杠为什么要注意?
答案:
① Uniform Resource Locator(统一资源定位符)。域名只是 URL 里的"主机名"那一段——域名回答"哪台主机",URL 还要回答"用什么协议取、取哪一个资源"。
② 协议名 / 主机名 / 端口号 / 路径 / 查询串 / 片段。片段(# 后面那段)服务器根本收不到,它只在浏览器里生效。
③ 路径和查询串原样装进 HTTP 的请求行(GET /path/page.html?q=1 HTTP/1.1);主机名先被 DNS 换成 IP 用于建连接,再作为 Host 头带上——因为一台服务器上可能挂着好几个网站,得靠它区分。
④ http 默认 80、https 默认 443(收 3.17)。"不写端口"=用协议的默认端口,不是"没有端口"。
⑤ 因为动态网站的路径是程序自己定的规则(路由)。/user/123 这种地址背后可能没有对应的文件,是程序现场生成了页面(4.1 动态内容)。只有静态网站才近似"路径 = 文件位置"。
⑥ 省掉了端口(补默认 80/443)和路径后半段(补默认文档名,通常是 index.html)。
⑦ 绝对 URL 从协议写起、自带全套;相对 URL 只写一部分,由浏览器拿"当前页面地址"当基准拼出来。站内用相对地址:换域名不用改文件、本地预览也能用。代价是层级算错就 404。
⑧ 空格写成 %20;中 先按 UTF-8 变成三个字节 E4 B8 AD,每个字节再写成 %XX,于是 中 → %E4%B8%AD,一个汉字 9 个字符。
⑨ 因为片段根本不在"发给服务器的部分"里——它归浏览器自己管。所以改它不会产生新请求,页面自然不重新加载,只是滚动一下。
⑩ 域名不区分大小写,路径区分大小写。结尾斜杠常被服务器区别对待(/about 与 /about/ 可能 301 互相跳),所以"本地能打开、上线 404"很多是它惹的。
十三、下一步学什么
- ☑ 请求Web网站的文件(HTTP) —— 本节把“地址”讲透了,也顺手把请求行(
GET /path?q=1 HTTP/1.1)这个下一站的主角指了出来。这就接着从这行往下写:报文还有哪几部分、服务器怎么回、那三位状态码怎么看,以及"服务器凭什么不记得你"(HTTP 的完整机制)。
相关笔记
- Web网站是如何构建的(Web网站) —— 上一站:URL 在那里初次露脸(
?要发、#不发),本节逐段拆开 - 制作Web网页(HTML) ——
<img src>与<a href>里写的都是 URL,"破图 404"就是相对路径算错 - 设计Web网页的外观(样式表) ——
<link href="style.css">同样是 URL;CSS 缓存与 URL 唯一性也有关 - 根据端口号分配数据到应用程序(端口号、已知端口号) —— URL 的第三段就是端口;80/443 这些已知端口的出处
- 网络的电话簿(域名解析、主机名、DNS) —— URL 的第二段(主机名)在这里被换成 IP
- 使用DNS自动获取IP地址(资源记录、DNS解析模块、递归查询) —— "谁去做解析"那个软件部件,本节第三节又用了一次
- 网络的具体结构(接口(端口)、链路) —— 老账提醒:URL 里的"端口号"是软件端口,不是设备上的物理接口
- 通信过程中所使用的语言(网络架构) —— URL 是应用层这一层里的"地址写法",与协议栈分层对应