一级代理
用户访问时只经过一层代理,它的架构图如下:
Client -> Proxy Server -> Backend Server
此时,代理服务器的配置一般如下:
server {
listen 80;
server_name foo.com;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://192.168.0.20:80;
}
}
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,但是该变量很容易就会被伪造,所以并不能完全信任。比如说:
curl http://foo.com -H "X-Forwarded-For: 1.1.1.1"
X-Forwarded-For
的值如下
1.1.1.1, 192.168.0.1
其中 192.168.0.1 是客户端 IP,客户端 IP 被追加到上游 X-Forwarded-For 的后面。此时,不能直接使用最左侧的 IP 当成客户端 IP,正确的做法是从右到左,找到第一个不是内网 IP 的合法 IP(不是代理服务器 IP)就是客户端的 IP。
多级代理
用户访问时经过了多层代理
Client -> Proxy1 -> Proxy2 -> ... -> Backend Server
出现这种情况有可能是用户使用了代理功能,或者网站架构本身就需要经过多层的代理。此时以上各配置项的含义发生了一些变化。
当 proxy_pass 使用了 IP 转发请求时,代理服务器就需要设置 Host
,因为这样才能访问到正确的网站。
# 每级代理都应该设置,除非 proxy_pass 使用了域名转发
proxy_set_header Host $http_host;
proxy_pass http://192.168.0.20:80;
而 X-Real-IP
和 X-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 攻击等风险。
# 应从右到左提取客户端 IP
X-Forwarded-For 'Invalid String <>, 1.1.1.1', 2.2.2.2, 3.3.3.3