代理服务器就是"替你去取"的那台中间机器:你的请求不直接发给 Web 服务器,而是先发给代理,由它代你去取、再把结果给你。它的特殊之处在于站在应用层——它看得懂 HTTP,所以能做的事远不止"转发":缓存省带宽、统一出口与管控、隐藏客户端、负载均衡、SSL 卸载、挡住攻击。它有两个方向:正向代理替客户端出面(服务器不知道你是谁,要你自己配)、反向代理替服务器出面(客户端根本不知道它存在,网站自己装的)。记住一句话就不会搞混:判断方向不看名字,看它替谁出面、藏起来的是谁。
本节把 4.4 讲 URL、4.5 讲 HTTP、4.6 讲缓存、4.7 讲 Cookie 这四笔账串起来用一次:
- 发给代理时,请求行里的 URL 会变成完整的(4.4 那条"域名不发送、只在
Host头里"的规矩,在这里出现了例外) - 代理自己也能缓存,所以 4.6 那些缓存头不只给浏览器看
- 代理看得见 Cookie(4.7 那张纸条),这既是它的能力,也是它的风险
一、代理是什么
代理(Proxy)= 替别人办事的中间人。 网络里的代理服务器,就是站在你和目标服务器之间、替你去取数据的那台机器。
平时你的请求是这样的:
浏览器 ──────────────────→ Web 服务器
配了代理之后:
浏览器 ──→ [代理服务器] ──→ Web 服务器
↑ 它替你去问
收 1.6 讲过分层。对照着看就很清楚:
| 路由器 | NAT | 代理服务器 | |
|---|---|---|---|
| 在哪一层 | 网络层 | 网络层(改地址) | 应用层 |
| 干什么 | 按 IP 找路 | 换掉地址让他能回来 | 代表你重新发一次请求 |
| 看得懂内容吗 | 看不懂 | 看不懂 | 完全看得懂(HTTP 头、URL、Cookie 全在它眼前) |
| 典型位置 | 网络出口 | 网络出口 | 客户端一侧 / 服务器一侧 |
一句话分清:NAT 换的是"地址",代理换的是"谁在问"。 NAT 是"在门口换个门牌号"(收 3.14),代理是"我替你去跑一趟"。
正因为看得懂内容,代理才能做缓存、过滤、改写、统计——这些路由器一件都做不了。
二、为什么要用代理
代理的价值可以归成六条,前三条是"客户端这一侧"的理由,后三条是"服务器这一侧"的理由:
| 理由 | 谁受益 | 说明 |
|---|---|---|
| 省带宽、加速 | 整个局域网 | 单位里 50 个人都要看同一个页面,代理取一次,50 个人共用(缓存,收 4.6) |
| 统一出口与管控 | 公司 / 学校 | 谁能上网、能上哪些站、什么时段能上,全在代理这里裁 |
| 隐藏客户端 | 客户端自己 | 外面只看到代理的 IP(收 3.13 全局地址与私有地址) |
| 负载均衡 | 服务器 | 把请求分给背后十台机器 |
| SSL 卸载 | 服务器 | 加密解密这件累活交给代理,后面的机器只处理明文 |
| 挡住攻击 / 加速分发 | 服务器 | 提前挡掉恶意流量;CDN 就是反向代理的规模化 |
顺带一提,你其实早就用过代理:抓包调试工具(Fiddler、Charles)本质就是"跑在你本机上的一个正向代理"——流量先经过它,它才有机会给你看。
三、正向代理与反向代理——本节最容易搞混的一处
(配图一)
名字只差一个字,站的位置却完全相反。判断方法只一条:看它替谁出面。
正向代理(Forward Proxy)——替客户端出面
- 站在客户端这一侧(常常就在你的局域网里,甚至就在你自己的电脑上)
- 你的浏览器要主动配置它:告诉浏览器"以后别自己去了,把请求交给它"
- 效果:服务器只看到代理,不知道真正访问它的是谁
- 典型场景:企业统一出口、上网管控、加速缓存、抓包调试
反向代理(Reverse Proxy)——替服务器出面
- 站在服务器这一侧(挂在机房的入口,网站自己装的)
- 你什么都不用配,也根本感觉不到它存在——你访问的还是那个域名
- 效果:客户端只看到代理,不知道背后站着几台机器
- 典型场景:负载均衡、SSL 卸载、CDN、静态资源加速、挡住攻击
对照表
| 正向代理 | 反向代理 | |
|---|---|---|
| 站在哪一侧 | 客户端这一侧 | 服务器这一侧 |
| 替谁出面 | 客户端 | 服务器 |
| 藏起来的是 | 客户端(服务器不知道你是谁) | 服务器(客户端不知道背后有几台) |
| 谁来配置 | 你要主动配(浏览器 / 系统 / 环境变量) | 网站自己装,你无感 |
| 常见用途 | 统一出口与管控、缓存加速、抓包调试 | 负载均衡、SSL 卸载、CDN、防攻击 |
你平时上的那些大网站,前面基本都站着反向代理(nginx、HAProxy、云厂商的负载均衡器)。
你请求 www.example.com,实际先到那台反向代理,由它决定这一趟交给后面哪一台服务器——而这些你完全看不到。收 1.7 讲"加机器就能扛更多访问"时,前面那道门就是它。
四、请求穿过代理时,报文变了什么
(配图二)
这是本节最值得亲手验证的一点。
平常的请求行:只有路径
直连时,请求行里只写路径,域名放在 Host 头里(收 4.4 第七节):
GET /index.html HTTP/1.1
Host: shop.example.com
发给代理的请求行:URL 是完整的
发给代理时不一样:
GET http://shop.example.com/index.html HTTP/1.1
Host: shop.example.com
为什么? 因为一台代理要替很多个目标服务。它必须从请求行本身就能看出"这一趟要去哪儿"——不能只靠 Host 头(同一条连接上可能连续问好几个不同的站)。所以在代理这条路上,URL 必须是完整的(这种写法叫 absolute-form)。
代理转发出去时:又把域名取下来
代理从完整 URL 里取出主机名 → 用 DNS 查出 IP(收 3.21)→ 建立一条属于它自己的连接 → 转发出去的请求行又变回只有路径:
GET /index.html HTTP/1.1
Host: shop.example.com
代理会加上的几个头
| 头 | 作用 |
|---|---|
Via |
这趟请求经过过哪些代理 |
X-Forwarded-For |
原始客户端的 IP(反向代理必加) |
X-Forwarded-Proto |
客户端原本用的是 http 还是 https |
X-Real-IP |
同 X-Forwarded-For,只放最后一个地址 |
反向代理之后,服务器看到的连接全部来自代理。想知道"真正是谁在访问",只能靠代理主动加上 X-Forwarded-For。
但它不可全信:客户端自己也带这个头来伪造,代理只是往后追加。所以规范做法是只信最靠近自己的那一台代理写入的值。
五、代理和缓存(收 4.6 那笔账)
代理本身也是一台"有资格缓存的中间人"。别人刚取过的东西,第二次它就直接给你,连目标服务器都不打扰(配图二那条绿色回环)。
这就解释了一件事:4.6 讲的 Cache-Control 不只给浏览器看,也给中间人看。
| 指令 | 意思 |
|---|---|
Cache-Control: public |
谁都能存(浏览器、代理、CDN 都可以) |
Cache-Control: private |
只有浏览器能存,中间人一律不许存 |
Cache-Control: no-store |
谁都别存,一次性的 |
Cache-Control: s-maxage=600 |
专门给共享缓存(也就是代理)规定的寿命 |
如果登录后的页面(带你的名字、购物车)被代理缓存了,下一个访问同一个地址的人就会看到你的内容——这就是"缓存污染"事故。
所以那些"因人而异"的响应,服务器一定会写 Cache-Control: private 或 no-store。这也是 4.7 的会话为什么要靠 Cookie 而不能靠缓存。
六、HTTPS 时代的代理:隧道
HTTP 是明文,代理什么都看得见。HTTPS 呢?
浏览器不会明文告诉代理"我要取哪个页面的内容",而是先发一条:
CONNECT shop.example.com:443 HTTP/1.1
这叫作建立隧道(tunnel)。之后双方只是原样转发字节,加密内容代理看不到。
但要注意它仍然知道:
- 你访问的是哪个域名(
CONNECT那一行里写着) - 你访问了多久、传了多大的数据
- 别随便用陌生的代理:HTTP 明文下,你的 URL、Cookie(4.7 那张纸条)、表单内容全在它眼前。免费代理是最便宜的窃取手段。
- 公司的代理可能看得见 https 内容:如果单位给你的电脑装了自签根证书,代理就能"中间拆开看、再重新加密"。所以公司设备上的 https 并不等于安全。
Secure标记的 Cookie 仍然要带走 HTTPS(收 4.7):隧道能保护内容,但域名和去向始终暴露。
七、动手 Demo:亲手做一个代理,看它眼里的世界
第 1 步 · 先看本机现在有没有配代理
netsh winhttp show proxy
(Windows 上还有一处:设置 → 网络和 Internet → 代理。两个地方是分开的,别弄混。)
第 2 步 · 写一个"会说话"的极简代理
它不真的转发,只把代理看到的第一行原样显示出来——这一下正是本节的重点。
新建 mini_proxy.py:
import socket, threading
def handle(conn):
data = conn.recv(65535)
if not data:
conn.close()
return
line = data.split(b"\r\n")[0].decode("utf-8", "ignore")
print("代理看到的第一行:", line)
parts = line.split()
if len(parts) < 2:
conn.close()
return
url = parts[1]
host = url.split("/")[2] if url.count("/") > 2 else url
path = "/" + url.split("/", 3)[3] if url.count("/") > 2 else "/"
body = ("我是代理服务器。\n\n"
"你发来的完整 URL 是:\n" + url + "\n\n"
"我替你拆成两部分:\n"
" 主机名 = " + host + "\n"
" 路径 = " + path + "\n")
raw = body.encode("utf-8")
head = ("HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain; charset=utf-8\r\n"
"Content-Length: " + str(len(raw)) + "\r\n\r\n")
conn.sendall(head.encode() + raw)
conn.close()
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("127.0.0.1", 8888))
s.listen(5)
print("代理已在 127.0.0.1:8888 等你(Ctrl+C 退出)")
while True:
conn, _ = s.accept()
threading.Thread(target=handle, args=(conn,), daemon=True).start()
第 3 步 · 起起来
python mini_proxy.py
第 4 步 · 用 curl 走一趟代理(最安全的验证方式)
curl -x http://127.0.0.1:8888 http://example.com
你会看到返回的内容变成了那段"我是代理服务器……",同时脚本窗口打印出一行:
代理看到的第一行: GET http://example.com/ HTTP/1.1
这就是本节的核心现场:发给代理的请求行里,URL 是完整的。(对比一下:平时直连时那一行只有
/。)
第 5 步 · 对比直连和过代理两种请求行
curl -v http://example.com/ # 直连:看 > GET / HTTP/1.1
curl -x http://127.0.0.1:8888 -v http://example.com/ # 过代理:看 > GET http://example.com/ HTTP/1.1
把两行并排看一眼,4.4 第七节的规矩和它的例外就同时在眼前了。
第 6 步 · 让浏览器也走一次(做完记得改回来)
Windows:设置 → 网络和 Internet → 代理 → 手动设置代理 → 地址填 127.0.0.1,端口填 8888 → 保存。
然后浏览器打开 http://example.com(必须是 http,不是 https)——页面会变成代理给你写的那段话,同时脚本窗口又打印一行。
不关的话,你这台机器上所有浏览器流量都会往那个已经关掉的端口发,表现为"上不了网"。
命令行里那条 curl -x 更安全——只影响这一次命令,所以推荐先用它。
八、30 秒实验
- 30 秒:
netsh winhttp show proxy—— 看一眼你这台机器现在有没有被谁"代着走"。 - 30 秒:
curl -x http://127.0.0.1:8888 -v http://example.com—— 在输出里找> GET http://…(完整 URL 那一行)。 - 30 秒:
curl -v http://example.com同样找>那一行 —— 两行一比,就是"直连 vs 过代理"的全部差别。
九、常见误区
- "代理和 VPN 是一回事。" —— 层次不同:代理通常只管某一种协议的流量(HTTP 代理只管网页),VPN 在网络层把全部流量接管(收 3.14 的隧道是另一回事)。
- "用了代理就匿名了。" —— 只是把 IP 换成了代理的。你的 Cookie(收 4.7)、浏览器特征、登录状态照样带着。
- "反向代理也要用户自己配。" —— 完全不用、也感觉不到。它是网站自己装在入口的,用户访问的还是同一个域名。
- "代理看不懂 HTTPS,所以用 HTTPS 就绝对安全。" —— 内容它看不到,但域名、流量大小、访问时间它都知道;而且公司装了根证书的代理能拆开看。
- "代理和 NAT 是一回事。" —— 完全不同层:NAT 在网络层换地址,代理在应用层替你重新发请求(第一节那张表)。
- "代理能让所有网站都快。" —— 只有被访问过、又允许被缓存的内容才快(收 4.6)。登录后的页面、
no-store的内容一点都不会快。 - "请求行里的 URL 一直就是只有路径。" —— 直连时是;发给代理时是完整的(本节第四节)。
- "
X-Forwarded-For可以当作真实 IP 用。" —— 客户端可以伪造它,只有最靠近你的那台代理写入的值才可信。 - "代理服务器只是转发,出不了大事。" —— 它是单点:它挂了整个局域网都上不了网;它也是隐私汇聚点:所有人的上网记录都经过它。
- "免费代理只是慢一点,没别的坏处。" —— HTTP 明文下,它能看到你所有的 URL、Cookie 和表单内容。这是最廉价的一种窃取手段。
十、动手自测
- 代理服务器站在哪一层?这一层给了它什么路由器没有的能力?
- 正向代理和反向代理怎么区分?各自藏起来的是谁?
- 反向代理用户需要配置吗?为什么?
- 平时直连时请求行里写什么?发给代理时又写什么?为什么会有这个差别?
- 代理转发给目标服务器时,会把请求行改回什么样?
X-Forwarded-For是干什么的?它可信吗?Cache-Control: private和public对代理分别意味着什么?为什么登录后的页面绝不能被代理缓存?- HTTPS 穿过代理时,代理还能知道什么、不能知道什么?
- 代理和 NAT 的本质差别是什么?
- 为什么说"免费代理"是最廉价的窃取手段?
答案:
① 站在应用层。它看得懂 HTTP(URL、头部、Cookie 全在眼前),所以能做缓存、过滤、改写、统计——这些网络层的路由器一样都做不了。② 看替谁出面:正向代理替客户端出面、藏起客户端(服务器不知道真正来访者是谁);反向代理替服务器出面、藏起服务器(客户端不知道背后有几台机器)。③ 不需要。反向代理是网站自己装在入口的,用户访问的还是那个域名,感觉不到它存在。④ 直连时只写路径(GET /index.html HTTP/1.1,域名在 Host 头里,收 4.4);发给代理时写完整 URL(GET http://shop.example.com/index.html HTTP/1.1)。因为一台代理要替很多目标服务,必须从请求行就能看出这趟要去哪儿。⑤ 代理从完整 URL 里取出主机名(再用 DNS 查 IP),转发出去的请求行又变回只有路径,Host 头照旧。⑥ 它记录原始客户端的 IP(反向代理之后服务器看到的全是代理,只能靠它知道真实来访者)。不可全信——客户端能自己伪造,只有最靠近你的那台代理写入的值才信。⑦ public = 谁都能存(浏览器、代理、CDN);private = 只有浏览器能存,中间人一律不许。登录后的页面因人而异,一旦被共享缓存存下,下一个人就会看到你的内容(缓存污染事故),所以必须 private 或 no-store。⑧ 能知道:你访问的域名(CONNECT 那行里写着)、访问时间、流量大小;不能知道:页面内容(隧道里是加密的)——除非中间装了根证书做拆解。⑨ NAT 在网络层换地址("在门口换个门牌号");代理在应用层替你重新发一次请求("我替你去跑一趟")。⑩ 因为 HTTP 是明文,代理能看到你所有的 URL、Cookie 和表单内容——Cookie 往往就是登录凭证(收 4.7),拿到它等于拿到你的登录状态。
十一、下一步学什么
- ☑ 审核公司员工浏览的Web网站(代理服务器的用途) —— 本节把"中间人"这一层真正用起来了:公司为什么能审核员工上网(收口 → 唯一的记录点)、审核的四层能见度、日志与名单两种做法、代理与防火墙的分别;以及 4.8 结尾那扇 HTTPS 的门被正面讲开——加密之后代理只剩"信封",想看到内容只有装根证书做 SSL 解密,而那条路有明确的合规门槛。
相关笔记
- 记录Web网站的文件(HTTPCookie) —— 上一节:代理看得见 Cookie 这张纸条,这是它的能力和它的风险所在
- 传输Web网站的文件(HTTP响应) —— 代理的缓存用的正是那一套缓存头;本节补上
public/private这半张表 - 请求Web网站的文件(HTTP) —— 代理转发时加上的
Via、X-Forwarded-For都是 HTTP 头部,规矩照旧 - Web网站的地址(URL) —— "域名不发送、只放在 Host 头"这条规矩,在发给代理时出现例外(完整 URL)
- Web网站是如何构建的(Web网站) —— "Web 服务器软件"那一件(nginx 既是门房,也常是反向代理本身)
- 从私有网络到互联网间的通信(NAT) —— 同样是"替身",但 NAT 在网络层换地址,代理在应用层换"谁在问"
- 互联网中使用的地址与私有网络中使用的地址(全局地址、私有地址) —— 正向代理"隐藏客户端"靠的就是外面只看到它的全局地址
- 网络的电话簿(域名解析、主机名、DNS) —— 代理拿到主机名之后,也要靠 DNS 查出 IP 才能代你去取
- 根据端口号分配数据到应用程序(端口号、已知端口号) —— 代理服务自己也要占一个端口(常见的 8080、3128)
- 用于构建网络的设备(网络设备) —— 设备按层归队:代理是应用层的设备
- 运行和管理服务器(服务器、云服务、内部部署) —— 反向代理就是"加机器扛访问量"时站在最前面那道门
- 通信过程中所使用的语言(网络架构) —— 代理的能力全部来自"它站在应用层、看得懂协议"