一、正向代理与反向代理的区别
一句话说清:
|
正向代理 |
反向代理 |
| 替谁办事 |
替客户端 |
替服务端 |
| 隐藏谁 |
隐藏客户端 |
隐藏服务端 |
| 部署在哪 |
客户端这一侧 |
服务端这一侧 |
| 客户端知道目标吗 |
知道 |
不知道(以为代理就是目标) |
| 典型场景 |
翻墙、公司内网访问外网 |
负载均衡、SSL 终止、CDN |
1 2 3 4 5
| 正向代理: 你 → [代理] → 谷歌 谷歌只看到代理 IP,不知道是你
反向代理: 用户 → [Nginx] → 后端服务 用户以为 Nginx 就是服务器,不知道后面还有一堆
|
二、核心疑问:Host 明明是目标地址,请求怎么跑到代理服务器去了?
这是每个学代理的人都会困惑的问题。来看一个典型的代理请求:
1 2 3
| GET http://ccs.kentonly.com/api/v1/contents HTTP/1.1 Host: ccs.kentonly.com Proxy-Authorization: Basic xxxxx
|
Host 头里写的是 ccs.kentonly.com,那这个请求凭什么能到达代理服务器?
答案是:Host 头只负责 HTTP 层,不负责 TCP 层。代理地址根本不在 HTTP 报文里。
代理地址在哪配?——不在 HTTP 协议里,在客户端配置里
HTTP 协议本身没有任何字段用来指定代理地址。代理的域名或 IP 是在操作系统、浏览器、或命令行层面配置的:
| 配置方式 |
在哪里设 |
| Windows 系统代理 |
设置 → 网络和 Internet → 代理 → 手动设置代理 |
| macOS 系统代理 |
系统设置 → 网络 → 高级 → 代理 |
| 浏览器代理 |
Chrome/Edge 设置 → 系统 → 打开计算机的代理设置 |
| curl 命令行 |
curl -x http://proxy:8080 |
| 环境变量 |
export http_proxy=http://proxy:8080 |
| 代码里 |
axios、requests 等库的 proxy 参数 |
这些配置告诉客户端一件事:TCP 连接不要连 Host 头里的域名,改连代理地址。
分两层看就清楚了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| ┌─────────────────────────────────────────────────┐ │ 第 1 层:TCP — 谁来决定连哪个 IP? │ │ │ │ 客户端配置了代理 proxy.example.com:8080 │ │ → DNS 解析 proxy.example.com → 拿到代理 IP │ │ → TCP 三次握手 → 和代理 IP:8080 建立连接 │ │ │ │ Host 头在这里完全没有参与! │ └─────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────┐ │ 第 2 层:HTTP — 跑在已经建好的 TCP 连接上 │ │ │ │ GET http://ccs.kentonly.com/api/v1/contents │ │ Host: ccs.kentonly.com │ │ │ │ 代理收到后,从请求行里提取出 ccs.kentonly.com │ │ 代理自己去 DNS 解析 → 连目标服务器 → 转发请求 │ │ 目标服务器返回响应 → 代理原路送回客户端 │ └─────────────────────────────────────────────────┘
|
用 curl 看得更直观:
1 2 3 4 5 6 7 8
| curl http://ccs.kentonly.com/api
curl -x http://proxy.example.com:8080 http://ccs.kentonly.com/api
|
总结:
1 2
| 不配代理:客户端 DNS 解析 Host 域名 → TCP 直连目标 配了代理:客户端 DNS 解析代理域名 → TCP 连代理 → HTTP 请求里写完整 URL,代理再转发
|
Host 头自始至终不参与 TCP 建连。 它就是告诉代理”帮我去哪儿”,而代理地址是客户端配置决定的,属于 TCP 层的事。
三、HTTP 协议如何支持代理访问
清楚了”代理地址不在 HTTP 里”之后,再看 HTTP 协议本身怎么支持代理。
明文代理:请求行写完整 URL
1 2 3 4 5 6 7 8 9 10
| 普通请求(无代理): GET /api/v1/contents HTTP/1.1 Host: ccs.kentonly.com ↑ 路径是相对的,Host 头告诉服务器用哪个虚拟主机
代理请求: GET http://ccs.kentonly.com/api/v1/contents HTTP/1.1 Host: ccs.kentonly.com Proxy-Authorization: Basic xxxxx ← 可选:代理需要认证时用 ↑ 路径是完整 URL,代理从中解析目标地址
|
区别只有请求行的格式——普通请求写 相对路径,代理请求写 绝对 URL。这是 HTTP/1.1 规范(RFC 7230)本身就支持的,不是额外的”代理协议”。
HTTPS 代理:CONNECT 隧道
HTTPS 是加密的,代理不能看内容,所以需要另一种方式——打一条 TCP 隧道:
1 2 3 4 5 6 7 8 9
| 客户端 → 代理: CONNECT ccs.kentonly.com:443 HTTP/1.1 Host: ccs.kentonly.com
代理 → 客户端: HTTP/1.1 200 Connection Established ← 隧道打通!
之后双方在这条隧道里直接传加密的 TLS 数据, 代理只是一个搬运工,完全看不懂内容。
|
两种模式对比:
| 模式 |
GET 完整 URL |
CONNECT 隧道 |
| 适用协议 |
HTTP(明文) |
HTTPS(加密) |
| 代理能看内容吗 |
✅ 能(可缓存、过滤、改写) |
❌ 不能(端到端加密) |
| 代理能缓存吗 |
✅ 可以 |
❌ 纯转发 |
| HTTP 版本 |
HTTP/1.0+ |
HTTP/1.1+ |
这就是为什么 curl -x http://proxy:8080 https://google.com —— 代理只知道你连了 google.com,但你搜了什么它完全看不见。CONNECT 打隧道,TLS 加密是端到端的。
四、完整流程图解
最后用一个完整的例子串联所有概念——浏览器配了代理后访问 http://ccs.kentonly.com:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| 你的电脑 代理服务器 proxy:8080 目标服务器 47.238.145.36:80 │ │ │ │ 1. 检查系统代理配置 │ │ │ 发现配置了 proxy:8080 │ │ │ │ │ │ 2. DNS 解析 "proxy" │ │ │ → 拿到代理 IP │ │ │ │ │ │ 3. TCP SYN ─────────────────→ │ │ │ ←───────────────── TCP SYN-ACK │ │ │ TCP ACK ───────────────────→ │ 三次握手完成,连接建立 │ │ │ │ │ 4. GET http://ccs.kentonly.com/ │ │ │ Host: ccs.kentonly.com │ │ │ ──────────────────────────────→ │ │ │ │ 5. 从请求行提取 ccs.kentonly.com │ │ DNS 解析 → 47.238.145.36 │ │ │ TCP SYN ────────────────→│ │ │ ←──────────────────── SYN-ACK│ │ │ TCP ACK ─────────────────→│ │ │ │ │ │ 6. GET / HTTP/1.1 │ │ │ Host: ccs.kentonly.com │ │ │ ────────────────────────────→│ │ │ │ │ │ 7. ←── HTTP/1.1 200 OK ────│ │ │ (index.html) │ │ │ │ │ 8. ←── HTTP/1.1 200 OK ────── │ │ │ (index.html) │ │
|
关键点:第 4 步发的 HTTP 请求里 Host 是 ccs.kentonly.com,但这个 TCP 包的目标 IP 是代理的 IP(第 3 步决定的),不是 ccs.kentonly.com 的 IP。HTTP 头里的 Host 和 IP 包头的目标 IP 是两码事。
总结
- 正向代理隐藏客户端,反向代理隐藏服务端——部署位置不同,替谁”瞒天过海”不同
- 代理地址不在 HTTP 协议里,在客户端配置里——浏览器设置、系统代理、环境变量、
curl -x,这些告诉客户端 TCP 该连谁。HTTP 报文里的 Host 只是告诉代理”最终去哪”
- HTTP 原生支持代理——明文用请求行写完整 URL,HTTPS 用 CONNECT 打隧道,都不是额外协议