- Nginx初认识之 Nginx重载配置文件、热部署和更新日志 (一)
- Nginx初认识之 Nginx搭建静态资源服务器 (二)
- Nginx初认识之 Nginx搭建反向代理服务器 (三)
- Nginx初认识之 GoAccess可视化实时监控access.log日志 (四)
- Nginx初认识之 Nginx支持PHP和TP5项目 (五)
- Nginx架构篇之 Nginx的进程结构和信号管理 (六)
- Nginx架构篇之 Nginx和Apache的比较 (七)
- Nginx HTTP模块篇 Nginx接收HTTP请求的流程 (八)
- Nginx HTTP模块篇 server_name指令和Nginx处理HTTP请求的11个阶段(九)
- Nginx HTTP模块篇 realip模块 (十)
- Nginx HTTP模块篇 rewrite模块 (十一)
- Nginx HTTP模块篇 limit_conn和limit_req模块 (十二)
- Nginx HTTP模块篇 access阶段的deny allow auth_basic auth_request指令 (十三)
- Nginx HTTP模块篇 precontent阶段的try_files和mirror指令 (十四)
- Nginx HTTP模块篇 content阶段的static模块 (十五)
- Nginx HTTP模块篇 日志记录之log模块和过滤模块 (十六)
- Nginx HTTP模块篇 Nginx中的常用变量 (十七)
- Nginx HTTP模块篇 Nginx防盗链——referer模块 (十八)
- Nginx优化篇 Nginx+php_fpm使用php及其配置优化 (十九)
- Nginx优化篇 Linux内核参数优化和nginx配置优化 (二十)
- Nginx优化篇 Linux内核参数、Nginx配置和php-fpm配置优化汇总 (二十一)
- Nginx优化篇 ab压力测试和监控 (二十二)
- Nginx优化篇 nginx+php-fpm压力测试实践 (二十三)
在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 参数才会安装该模块。安装完后,可以使用以下指令:
set_real_ip_from # 指定可信地址,这里的可信地址是上一级代理,也就是直接访问上游服务的代理的ip;可以指定多行该指令;如果不使用该指令,就无法获取到起始客户端IP
real_ip_header # 告知Nginx真实客户端IP从哪个请求头获取。默认是X-Real-IP。但我们一般设置为X-Forwarded-For。
real_ip_recursive off; # 是否递归解析,off表示默认从最后一个地址开始解析。例如 X-Forwarded-For 是 1.1.1.1,2.2.2.2,3.3.3.3,那么off取的是3.3.3.3,根据我们上面讲的,这里真实客户端IP应该是1.1.1.1才对。所以我们一般设置为on。如果只有一层代理,on和off都无所谓。
变量
$realip_remote_addr # 上一级代理的ip
$realip_remote_port # 上一级代理的端口
$remote_addr # 如果不使用realip模块,$remote_addr是上一级代理的ip。使用了realip模块,$remote_addr是real_ip_header指令指定的起始客户端ip
这些指令和变量都是在终端服务写的,不要写在了代理服务中。
下面我们做一个试验,做一个两层反向代理请求:
客户端(A)在本地 : 223.73.208.20
两层反向代理和上游服务都在203.195.165.55
代理(B1): 203.195.165.55:8089
代理(B2): 203.195.165.55:8080 # 也就是B1的上游服务
终端上游服务©: 203.195.165.55:8088/remote_addr # 也就是B2的上游服务
Nginx配置如下:
# 定义终端上游服务
upstream rd {
server 203.195.165.55:8088;
}
# 定义代理B1的上游服务(代理B2)
upstream rd2 {
server 203.195.165.55:8080;
}
# 代理B2
server {
listen 8080;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 必须要定义,否则realip无法通过X-Forwarded-For请求头获取起始客户端ip
proxy_pass http://rd/remote_addr; #访问终端上游服务的remote_addr页面
}
}
# 代理B1
server {
listen 8089;
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 必须要定义,否则realip无法通过X-Forwarded-For请求头获取起始客户端ip
proxy_pass http://rd2;
}
}
# 终端上游服务
server {
listen 8088;
set_real_ip_from 203.195.165.55; # 指定了两个可靠IP,第二个是一个ip端
set_real_ip_from 223.73.208.0/24;
#real_ip_header X-Real-IP;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
location /remote_addr {
# 返回状态码200 以及一些信息。
return 200 "Your remote_addr is $remote_addr\nYour realip_remote_addr is $realip_remote_addr\nYour realip_remote_port is $realip_remote_port\nYour X-Forwarded-For $http_x_forwarded_for";
}
}
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
张柏沛IT技术博客 > Nginx HTTP模块篇 realip模块 (十)