在nginx中,我们可以通过 $remote_addr 变量来获取客户端的IP。获取了客户端IP之后,我们可以做很多事情如限速限流等。
但是如果 客户端A 通过 反向代理B 访问到 上游服务C ,假设上游服务是我们的nginx服务,那么上游服务 $remote_addr 获取到的是反向代理的IP,因为直接访问C的是反向代理B而不是客户端A。
此时我们如何去获取客户端A的IP。
在这里,我们要介绍header请求头的X-Forwarded-For和X-Real-IP
X-Forwarded-For
如果 A 直接访问 C,那么A的请求头中是没有 X-Forwarded-For 这个项的。
如果 A 通过代理B 再去访问 C ,此时B发向C的请求头中才会有 X-Forwarded-For 这个项,其内容是A的ip。
如果 A 通过代理B 访问代理B2 再访问C,此时到达C的请求头中的 X-Forwarded-For 的内容是 “A的ip,B的ip”;也就是说,经过多层代理的话,X-Forwarded-For会叠加经过的ip,每经过一层ip,代理服务器会叠加上一级访问者的ip,但是不会叠加自己的ip。
所以假如 A 的ip为1.1.1.1,B的ip为2.2.2.2,C的ip为192.168.5.100
那么 A通过代理B访问到C ,最终到达C的X-Forwarded-For 内容为: 1.1.1.1 (不含2.2.2.2,因为B不会在X-Forwarded-For中添加自己的ip),C就可以在X-Forwarded-For中获取到真实客户端A的IP,当然C也可以通过 $remote_addr 获取到代理B的ip。
如果 A通过代理B访问代理B2访问C,则:
A的请求头中X-Forwarded-For:无
B的请求头中X-Forwarded-For:1.1.1.1
B2的请求头中X-Forwarded-For:1.1.1.1,2.2.2.2
C接受到的请求中的请求头:X-Forwarded-For:1.1.1.1,2.2.2.2
当然,代理可以决定加不加 X-Forwarded-For 。有些正向代理为了隐藏真实客户端IP会不设置X-Forwarded-For这个请求头。
但是一般反向代理会按照上面的规则设置 X-Forwarded-For 以方便知道请求经过了哪些IP。
X-Real-IP
X-Real-IP 就简单多了,无论经过多少层代理,X-Real-IP记录的都是原始客户端A的ip。
但是在使用代理的时候,代理服务器会遵守规则去添加 X-Forwarded-For ,可是X-Real-IP则不会作为标准要求代理去添加。
所以在上游服务器获取起始客户端IP一般是使用 X-Forwarded-For 而不是 X-Real-IP。
正向代理和反向代理
正向代理是,假如我要访问谷歌,但是国外网站国内访问不了,此时我们会主动使用VPN这样的代理服务去访问谷歌。这个代理是我们客户端主动找的中间节点,这个就是正向代理。
反向代理是,假如我要访问百度,百度有很多的服务器,百度为了方便我们访问会设置代理服务器,然后我们访问的是百度的代理,代理服务根据一定算法把我们的请求分配到百度其中一台主机上,这样做是为了负载均衡以及根据地区选择最近的节点加速访问。这个代理是上游服务端百度为了方便我们访问而设,所以这个就是反向代理。
正向代理和反向代理的原理都一样,关键是由客户端设置的还是服务端设置的。客户端设置的就是正向代理,服务端设置的是反向代理。
realip模块
该模块的作用是帮助我们在使用了代理的情况下获取客户真实ip。
该模块默认不编译进nginx二进制文件中,需要在编译时指定 --with-http_realip_module 参数才会安装该模块。安装完后,可以使用以下指令:
变量
这些指令和变量都是在终端服务写的,不要写在了代理服务中。
下面我们做一个试验,做一个两层反向代理请求:
客户端(A)在本地 : 223.73.208.20
两层反向代理和上游服务都在203.195.165.55
代理(B1): 203.195.165.55:8089
代理(B2): 203.195.165.55:8080 # 也就是B1的上游服务
终端上游服务(C): 203.195.165.55:8088/remote_addr # 也就是B2的上游服务
Nginx配置如下:
proxy_set_header 可以在代理服务发送请求时添加相应的header头。上面通过proxy_set_header添加了X-Forwarded-For/X-Real-Ip/Host这三个header头。
如果不添加这几个header头,终端服务就不能通过 X-Forwarded-For 或者 X-Real-IP 获取起始客户端的IP了。
# 重新加载配置
nginx -s reload
在我本地 223.73.208.20 主机的cmd中发送一条对代理B1的请求,B1会请求B2再请求到C。得到的信息为
Your remote_addr is 223.73.208.20 # 起始客户端IP
Your realip_remote_addr is 203.195.165.55 # 直接请求终端的代理IP,即B2的ip
Your realip_remote_port is 38004
Your X-Forwarded-For 223.73.208.20, 203.195.165.55 # X-Forwarded-For请求头的值,这里是起始客户端IP和代理B1的IP