前言
随着nginx的迅速崛起,越来越多公司将apache更换成nginx. 同时也越来越多人使用nginx作为负载均衡和反向代理, 并且代理前面可能还加上了CDN加速,但是随之也遇到一个问题:后端应用如何获取用户的真实IP地址。
❝nginx作为高性能反向代理服务器,在传递真实IP到后端的过程中,需要使用到
ngx_http_proxy_module
模块,此模块默认会编译。
常见配置
...
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream ywnm {
server 192.168.0.1;
server 192.168.0.2;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://ywnm;
# 下面三行为常见反向代理传递真实客户端IP的配置,如果配置在http{}中,则全局对所有server生效
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
...
在nginx中,我们可以通过$remote_addr
变量来获取客户端的IP。获取了客户端IP之后,我们可以做很多事情如限速限流、用户画像等。但是如果客户端A通过反向代理B访问到后端服务C,假设后端服务是我们的nginx服务,那么后端服务$remote_addr
获取到的是反向代理B的IP,因为直接访问C的是反向代理B而不是客户端A。如果在客户端A和反向代理B之间有CDN加速或其他代理,那这个真实客户端A的IP又是如何传送到后端服务C的呢?
配置详解
proxy_set_header
在传递给后端服务器的请求头中,可以使用proxy_set_header 重新定义或添加字段。一般我们使用 proxy_set_header 向后端服务器传递一些必要的信息。
Host
Host 是表明请求的主机名。默认情况下,Nginx 向后端服务器发送请求时,请求头中的 Host 字段是后端真实服务器的IP和端口。如果我们想让传递给后端服务器的 Host 字段,包含的是用户访问反向代理时使用的域名,就需要通过 proxy_set_header 设置 Host 字段,值可以为$host
或$http_host
,区别是前者只包含IP,而后者包含IP和端口号。X-Real-IP
经过反向代理后,后端服务器无法直接拿到客户端的 ip,也就是说,在应用中使用request.getRemoteAddr()
获得的是 Nginx 的地址。通过proxy_set_header X-Real-IP $remote_addr;
,将客户端的 ip 添加到了 HTTP header中,让应用可以使用request.getHeader(“X-Real-IP”)
获取客户端的真实ip。X-Forwarded-For如果配置了多层反向代理,当一个请求经过多层代理到达后端服务器时,后端服务器通过
X-Real-IP
获得的就不是客户端的真实IP了。那么这个时候就要用到 X-Forwarded-For ,设置 X-Forwarded-For 时是增加,而不是覆盖,从客户的真实IP为起点,穿过多层级代理 ,最终到达后端服务器,都会被记录下来。
实战
下面通过实验,揭秘上述参数变量的工作原理
环境
角色 | ip | 软件及版本 |
---|---|---|
客户端 | 192.168.124.101 | nginx1.22.1 |
反向代理A | 192.168.124.170 | nginx1.22.1 |
反向代理B | 192.168.124.171 | nginx1.22.1 |
反向代理C | 192.168.124.172 | nginx1.22.1 |
后端web服务器 | 192.168.124.173 | nginx1.22.1 |
客户端配置域名解析文件C:\Windows\System32\drivers\etc\hosts
192.168.124.170 www.ywnm.com
X-Real-IP
三个代理节点及后端web服务的nginx初始配置如下(基础配置部分省略):
反向代理A
http {
...
log_format main '$remote_addr + "$http_host" + "$http_x_forwarded_for" + "$http_x_real_ip"';
access_log logs/access.log main;
server {
listen 80;
server_name www.ywnm.com;
location / {
proxy_pass http://192.168.124.171;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
...
}
}
反向代理B
http {
...
log_format main '$remote_addr + "$http_host" + "$http_x_forwarded_for" + "$http_x_real_ip"';
access_log logs/access.log main;
server {
listen 80;
server_name 192.168.124.171;
location / {
proxy_pass http://102.168.124.172;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
...
}
}
反向代理C
http {
...
log_format main '$remote_addr + "$http_host" + "$http_x_forwarded_for" + "$http_x_real_ip"';
access_log logs/access.log main;
server {
listen 80;
server_name 192.168.124.172;
location / {
proxy_pass http://102.168.124.173;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
...
}
}
后端web服务器
http {
...
log_format main '$remote_addr + "$http_host" + "$http_x_forwarded_for" + "$http_x_real_ip"';
access_log logs/access.log main;
server {
listen 80;
server_name 192.168.124.173;
location / {
root html/;
index index.html index.htm;
}
...
}
}
只在反向代理A设置X-Real-IP
三个节点都注释掉proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
,并在节点BC上注释掉proxy_set_header X-Real-IP $remote_addr;
,在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "-" + "192.168.124.101"
节点C:192.168.124.171 + "www.ywnm.com" + "-" + "192.168.124.101"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "-" + "192.168.124.101"
只在反向代理B设置X-Real-IP
在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "-" + "-"
节点C:192.168.124.171 + "www.ywnm.com" + "-" + "192.168.124.170"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "-" + "192.168.124.170"
只在反向代理C设置X-Real-IP
在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "-" + "-"
节点C:192.168.124.171 + "www.ywnm.com" + "-" + "-"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "-" + "192.168.124.171"
只在反向代理A/B设置X-Real-IP
在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "-" + "192.168.124.101"
节点C:192.168.124.171 + "www.ywnm.com" + "-" + "192.168.124.170"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "-" + "192.168.124.170"
只在反向代理A/C设置X-Real-IP
在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "-" + "192.168.124.101"
节点C:192.168.124.171 + "www.ywnm.com" + "-" + "192.168.124.101"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "-" + "192.168.124.171"
只在反向代理B/C设置X-Real-IP
在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "-" + "-"
节点C:192.168.124.171 + "www.ywnm.com" + "-" + "192.168.124.170"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "-" + "192.168.124.171"
所有代理节点都设置X-Real-IP
在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "-" + "192.168.124.101"
节点C:192.168.124.171 + "www.ywnm.com" + "-" + "192.168.124.170"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "-" + "192.168.124.171"
❝总结
X-Real-IP只是一个变量,后面的设置会覆盖前面的设置 我们一般只在第一个代理设置 proxy_set_header X-Real-IP $remote_addr;
,然后在应用端直接引用$http_x_real_ip
即可X-Real-IP的值总为1个
X-Forward-For
三个代理节点及后端web服务的nginx初始配置同上
只在反向代理A设置X-Forward-For
三个节点都注释掉proxy_set_header X-Real-IP $remote_addr;
,并在节点BC上注释掉proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
,在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "192.168.124.101" + "-"
节点C:192.168.124.171 + "www.ywnm.com" + "192.168.124.101" + "-"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "192.168.124.101" + "-"
只在反向代理B设置X-Forward-For
在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "-" + "-"
节点C:192.168.124.171 + "www.ywnm.com" + "192.168.124.170" + "-"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "192.168.124.170" + "-"
只在反向代理C设置X-Forward-For
在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "-" + "-"
节点C:192.168.124.171 + "www.ywnm.com" + "-" + "-"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "192.168.124.171" + "-"
只在反向代理A/B设置X-Forward-For
在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "192.168.124.101" + "-"
节点C:192.168.124.171 + "www.ywnm.com" + "192.168.124.101, 192.168.124.170" + "-"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "192.168.124.101, 192.168.124.170" + "-"
只在反向代理A/C设置X-Forward-For
在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "192.168.124.101" + "-"
节点C:192.168.124.171 + "www.ywnm.com" + "192.168.124.101" + "-"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "192.168.124.101, 192.168.124.171" + "-"
只在反向代理B/C设置X-Forward-For
在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "-" + "-"
节点C:192.168.124.171 + "www.ywnm.com" + "192.168.124.170" + "-"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "192.168.124.170, 192.168.124.171" + "-"
所有代理节点都设置X-Forward-For
在客户端访问www.ywnm.com,各节点日志如下:
节点A:192.168.124.101 + "www.ywnm.com" + "-" + "-"
节点B:192.168.124.170 + "www.ywnm.com" + "192.168.124.101" + "-"
节点C:192.168.124.171 + "www.ywnm.com" + "192.168.124.101, 192.168.124.170" + "-"
后端web服务器:192.168.124.172 + "www.ywnm.com" + "192.168.124.101, 192.168.124.170, 192.168.124.171" + "-"
❝总结
X-Forwarded-For是一个可叠加的过程,代理会把 $remote_addr
加入到X-Forwarded-For,发送到下一层我们看到在三层代理情况下无论如何设置,后端web服务器不可能从 $http_x_forwarded_for
拿到与它直连的代理服务器的ip(反向代C ip),此时我们可以使用$remote_addr
(远程ip,表示直连的那台代理)在代理过程中至少有一个代理设置了 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
否则后面代理或者应用服务器无法获得相关信息想要获取客户端IP(必须要在反向代理A设置了 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
我们取第一IP就好了)

优网科技秉承"专业团队、品质服务" 的经营理念,诚信务实的服务了近万家客户,成为众多世界500强、集团和上市公司的长期合作伙伴!
优网科技成立于2001年,擅长网站建设、网站与各类业务系统深度整合,致力于提供完善的企业互联网解决方案。优网科技提供PC端网站建设(品牌展示型、官方门户型、营销商务型、电子商务型、信息门户型、DIY体验、720全景展厅及3D虚拟仿真)、移动端应用(手机站、APP开发)、微信定制开发(微信官网、微信商城、企业微信)、微信小程序定制开发等一系列互联网应用服务。