HTTP 代理详解:CONNECT 方法、认证机制与缓存策略

HTTP 代理,说白了就是客户端和服务器之间的“中间人”。你请求一个网页,不是直接去找目标服务器,而是先把请求发给代理,由代理帮你转发。这层中间人能做很多事情:缓存、过滤、匿名、加速……今天我们就来拆开看看,HTTP 代理到底是怎么工作的。

一、HTTP CONNECT 方法——建立隧道

普通的 HTTP 代理只能处理明文 HTTP 请求。但 HTTPS 流量是加密的,代理没法直接读取。那怎么办?

答案就是 CONNECT 方法。它让代理建立一个透明的 TCP 隧道,客户端通过这个隧道直接和目标服务器通信。代理只负责转发字节流,不关心内容。

核心流程:

  1. 客户端向代理发送 CONNECT 请求,指定目标主机和端口。
  2. 代理与目标服务器建立 TCP 连接。
  3. 代理返回 200 Connection Established。
  4. 之后,客户端和目标服务器之间所有数据都通过代理透传。

举个例子,客户端要访问 https://example.com:443:

CONNECT example.com:443 HTTP/1.1
Host: example.com:443
Proxy-Authorization: Basic base64(user:pass)

HTTP/1.1 200 Connection Established

嗯,这里要注意:CONNECT 只建立隧道,不解析数据。所以 HTTPS 的证书校验、加密握手,全在客户端和服务器之间完成,代理插不上手。

我个人的习惯: 在调试代理问题时,我会先用 curl -v -x proxy:port -p https://target.com 测试 CONNECT 是否正常。如果返回 200 但后续握手失败,多半是代理限制了端口或目标地址。

二、HTTP 代理的认证机制

代理不能随便让人用,对吧?尤其是企业内网,得控制谁可以访问。HTTP 代理支持两种主流认证方式:Basic 和 Digest。

2.1 Basic 认证

最简单,也最不安全。客户端把用户名和密码拼成 user:pass,然后 Base64 编码,放在 Proxy-Authorization 头里。

Proxy-Authorization: Basic dXNlcjpwYXNz

Base64 不是加密,是编码。随便抓个包就能解码出来。所以 Basic 认证只适合内网或配合 HTTPS 使用。

我曾经踩过的坑: 有一次客户反馈代理认证失败,查了半天发现是密码里包含了冒号。Basic 认证用冒号分隔用户名和密码,密码里的冒号会导致解析错误。后来我们强制要求密码不能包含冒号,或者用 URL 编码转义。

2.2 Digest 认证

Digest 认证比 Basic 安全一些。它不直接传密码,而是用 MD5 哈希计算一个摘要。流程是这样的:

  1. 客户端请求资源,代理返回 407 Proxy Authentication Required,带上 Proxy-Authenticate 头,包含 realm、nonce 等信息。
  2. 客户端用密码、nonce、HTTP 方法、URI 等计算哈希值,放在 Proxy-Authorization 头里。
  3. 代理验证哈希值,通过则放行。

看个例子:

# 代理返回的挑战
Proxy-Authenticate: Digest realm="proxy@example.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"

# 客户端响应
Proxy-Authorization: Digest username="user", realm="proxy@example.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", uri="/", response="7cfff3f3f3b3b3b3b3b3b3b3b3b3b3b3", opaque="5ccc069c403ebaf9f0171e9517f40e41"

Digest 认证虽然比 Basic 安全,但也不是无懈可击。MD5 哈希可以被暴力破解,而且它不保证消息完整性。现在更推荐用 HTTPS + Basic 或者更现代的认证方案。

特性 Basic 认证 Digest 认证
安全性 低(明文传输密码) 中(哈希传输)
实现复杂度 简单 中等
适用场景 内网、HTTPS 隧道 需要一定安全性的场景
性能开销 低 中(需要计算哈希)

三、HTTP 代理的缓存策略

代理缓存是提升性能的关键。你想想看,如果 100 个人都请求同一个资源,代理只需要从源站拉一次,后面 99 次直接从缓存返回,多快。

但缓存不是乱来的。HTTP 定义了一套缓存控制机制,代理必须遵守。

3.1 缓存控制头

服务器通过 Cache-Control 头告诉代理和客户端怎么缓存。常用的指令有:

  • public:响应可以被任何缓存(包括代理)缓存。
  • private:响应只能被客户端缓存,代理不能缓存。
  • no-cache:缓存前必须先验证资源是否过期。
  • no-store:禁止缓存,完全不存。
  • max-age=3600:缓存有效期 3600 秒。
  • s-maxage=3600:只对代理缓存生效,覆盖 max-age。

举个例子:

Cache-Control: public, max-age=86400

这个响应可以被代理缓存,有效期 24 小时。

3.2 缓存验证

缓存过期了怎么办?代理可以用 If-Modified-Since 或 If-None-Match 头向源站验证资源是否更新。

  • Last-Modified / If-Modified-Since:基于时间戳。服务器返回 304 Not Modified 表示资源没变,代理继续用缓存。
  • ETag / If-None-Match:基于资源指纹。ETag 是服务器生成的唯一标识,资源变了 ETag 就变。

我个人更推荐 ETag,因为时间戳有时不准(比如文件修改了但内容没变)。

3.3 缓存策略实战

在企业代理中,缓存策略需要平衡性能和新鲜度。我见过不少团队把缓存时间设得太长,结果用户看到的是过时的页面。

我的建议:

  • 静态资源(图片、CSS、JS)可以设 max-age=31536000(一年),配合版本号或哈希文件名。
  • API 响应用 no-cache 或短 max-age(比如 60 秒)。
  • HTML 页面用 no-cache,确保每次请求都验证。

四、知识体系图

下面这张图总结了 HTTP 代理的核心逻辑:

HTTP 代理核心逻辑 客户端 HTTP 代理 目标服务器 CONNECT 隧道建立 认证机制 (Basic / Digest) 缓存策略 (Cache-Control / ETag) 代理作为中间人,处理 CONNECT 隧道、认证和缓存 客户端与目标服务器之间的所有请求都经过代理转发

五、避坑指南

最后分享几个我实际工作中遇到的坑:

  • CONNECT 超时:代理和目标服务器建立连接有时会超时,尤其是跨地域访问。我建议在代理配置里调大 connect_timeout,比如 30 秒。
  • 认证缓存:有些代理会缓存认证结果,导致用户改了密码后还能用旧密码访问。解决办法是在代理配置里关闭认证缓存,或者设置较短的缓存时间。
  • 缓存污染:如果代理缓存了动态内容,用户可能看到别人的数据。一定要根据 Cache-Control 头正确判断哪些内容可以缓存。
  • Digest 认证的 nonce 过期:nonce 有时效性,过期后客户端需要重新认证。如果客户端没有正确处理 407 响应,就会陷入认证循环。

重要提醒: 在生产环境中,不要只依赖 Basic 认证。即使在内网,也建议用 HTTPS 加密代理通信,或者使用更安全的认证方式(如 NTLM、Kerberos)。

好了,HTTP 代理的核心内容就这些。CONNECT 方法、认证机制、缓存策略,这三块搞懂了,你就能应对大部分代理场景。下次遇到代理问题,记得先抓包看看请求和响应头,问题往往就藏在里面。