一级代理

用户访问时只经过一层代理,它的架构图如下:

  1. Client -> Proxy Server -> Backend Server

此时,代理服务器的配置一般如下:

  1. server {
  2. listen 80;
  3. server_name foo.com;
  4. location / {
  5. proxy_set_header Host $host;
  6. proxy_set_header X-Real-IP $remote_addr;
  7. proxy_set_header X-Forwarded-Proto $scheme;
  8. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  9. proxy_pass http://192.168.0.20:80;
  10. }
  11. }

Host

http_host 是客户端请求时的域名,代理服务器将该域名转发到后端服务器。如果后端服务器有多个不同域名的站点,host 转发是必须的。

X-Real-IP

remote_addr 是指与当前代理服务器建立 TCP 连接的客户端 IP, remote_addr 不能伪造,所以后端可以安全地使用该变量来获取客户端 IP。

X-Forwarded-Proto

scheme 是客户端请求时的协议,代理服务器通过设置 X-Forwarded-Proto ,将协议信息传递到后端服务器。如果后端服务器需要判断用户访问的是 HTTP 还是 HTTPS,这个变量可以用于判断。

X-Forwarded-For

proxy_add_x_forwarded_for 会记录从客户端到当前服务器所经过的所有上游设备的 IP,值得注意的是,它并不包括当前的代理服务器 IP,因为它只包含所有上游服务器的 IP。
后端可以通过 X-Forwarded-For 变量来获取客户端的 IP,但是该变量很容易就会被伪造,所以并不能完全信任。比如说:

  1. curl http://foo.com -H "X-Forwarded-For: 1.1.1.1"

X-Forwarded-For 的值如下

  1. 1.1.1.1, 192.168.0.1

其中 192.168.0.1 是客户端 IP,客户端 IP 被追加到上游 X-Forwarded-For 的后面。此时,不能直接使用最左侧的 IP 当成客户端 IP,正确的做法是从右到左,找到第一个不是内网 IP 的合法 IP(不是代理服务器 IP)就是客户端的 IP。

多级代理

用户访问时经过了多层代理

  1. Client -> Proxy1 -> Proxy2 -> ... -> Backend Server

出现这种情况有可能是用户使用了代理功能,或者网站架构本身就需要经过多层的代理。此时以上各配置项的含义发生了一些变化。
当 proxy_pass 使用了 IP 转发请求时,代理服务器就需要设置 Host ,因为这样才能访问到正确的网站。

  1. # 每级代理都应该设置,除非 proxy_pass 使用了域名转发
  2. proxy_set_header Host $http_host;
  3. proxy_pass http://192.168.0.20:80;

X-Real-IPX-Forwarded-Proto 则代表上游服务器的 IP 和请求协议,注意它不再表示客户端的 IP 和请求协议。
X-Forwarded-For 的含义依然不变,还是记录了从客户端 IP 到后端所经过的所有设备 IP。此时,要获取客户端 IP,只能从该变量中获取,获取方法跟一级代理中所说的一样。

总结

  • 如果只有一级代理时,可以安全地通过 X-Real-IP 或者 X-Forwarded-For 变量获取客户端 IP。
  • 如果是多级代理时,只能通过 X-Forwarded-For 变量获取客户端 IP。

值得注意的是,为了防止客户端伪造 IP,应从右到左检查 X-Forwarded-For 变量,找到第一个非局域网 IP 和非代理服务器 IP 就是客户端 IP。同时,还得注意 IP 格式的判断,以防止 SQL 注入或 XSS 攻击等风险。

  1. # 应从右到左提取客户端 IP
  2. X-Forwarded-For 'Invalid String <>, 1.1.1.1', 2.2.2.2, 3.3.3.3

参考