点解此处查看视频教程
当你的网站使用多层Nginx
代理(如CDN
+ 负载均衡 + 后端服务器),客户端真实IP总是被覆盖?日志里全是代理服务器的IP?我们应该如何在这种情况下透传客户端真实IP
呢?
环境
网络架构图

在proxy-3
上准备后端程序并启动,后端服务将监听在8080
端口。
1
2
3
|
wget -O /opt/http_server https://tools.snoopyops.top/file/http_server
chmod a+x /opt/http_server
/opt/http_server
|
所有服务器Nginx
日志格式均为默认格式。
1
2
3
|
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
|
remote_addr
在Nginx
日志中第一个字段就表示remote_addr
,先对Nginx
不做多余配置,我们观察获取到的remote_addr
地址。
配置Proxy_3
,将来自test.snoopyops.top
的所有请求都转发到本机的8080
端口。
1
2
3
4
5
6
7
8
9
|
server {
listen 80;
server_name test.snoopyops.top;
location / {
proxy_pass http://172.31.36.28:8080;
proxy_set_header Host $http_host;
}
}
|
配置Proxy_2
,将来自test.snoopyops.top
的所有请求都转发到Proxy_3
上。
1
2
3
4
5
6
7
8
9
|
server {
listen 80;
server_name test.snoopyops.top;
location / {
proxy_pass http://172.31.36.28;
proxy_set_header Host $http_host;
}
}
|
配置Proxy_1
,同理将来自test.snoopyops.top
的所有请求都转发到Proxy_2
上。
1
2
3
4
5
6
7
8
9
|
server {
listen 80;
server_name test.snoopyops.top;
location / {
proxy_pass http://172.30.144.202;
proxy_set_header Host $http_host;
}
}
|
做完域名解析后,我们访问test.snoopyops.top
后,依次查看所有服务器的Nginx
日志。
在Proxy_1
的Nginx
日志中,我们通过remote_addr
获取到了客户端的真实IP
。
1
|
219.143.190.117 - - [03/Mar/2025:11:36:35 +0800] "GET / HTTP/1.1" 200 52 "-" "curl/8.4.0" "-"
|
在Proxy_2
的Nginx
日志中,我们通过remote_addr
获取到的是Proxy_1
的IP
。
1
|
172.18.6.10 - - [03/Mar/2025:11:36:35 +0800] "GET / HTTP/1.0" 200 52 "-" "curl/8.4.0" "-"
|
在Proxy_3
的Nginx
日志中,我们通过remote_addr
获取到的是Proxy_2
的IP
。
1
|
172.30.144.202 - - [03/Mar/2025:11:36:35 +0800] "GET / HTTP/1.0" 200 52 "-" "curl/8.4.0" "-"
|
由此我们可以总结出:remote_addr
这个变量表示的是与Nginx
服务器直接建立TCP
连接的客户端的IP
地址。客户端是直接与Proxy_1
建立连接的,所以Proxy_1
可以通过remote_addr
获取到客户端真实IP
,而与Proxy_2
建立连接的是Proxy_1
,所以他就只能记录Proxy_1
的IP地址了。
在没有经过多层代理的情况下,我们可以通过remote_addr
获取客户端真实IP
。
X-Real-IP
既然我们在Proxy_1
上可以通过remote_addr
获取到客户端的真实IP
,那么我们可以在Proxy_1
上定义一个X-Real-IP
的请求头,将remote_addr
赋值给X-Real-IP
。并将X-Real-IP
一直传递到后端服务,这样后端服务就可以通过获取X-Real-IP
的值达到获取客户端的真实IP
的目的了。
在Proxy_1
上,我们做如下配置,将获取到的remote_addr
的值赋值给X-Real-IP
。
1
2
3
4
5
6
7
8
9
10
11
|
server {
listen 80;
server_name test.snoopyops.top;
location / {
proxy_pass http://172.30.144.202;
proxy_set_header Host $http_host;
# 添加请求X-Real-IP头,并将remote_addr赋值给X-Real-IP
proxy_set_header X-Real-IP $remote_addr;
}
}
|
我们在Proxy_2
中,获取proxy_1
定义的X-Real-IP
,并赋值给自己的给自己定义的X-Real-IP
请求头。
1
2
3
4
5
6
7
8
9
10
|
server {
listen 80;
server_name test.snoopyops.top;
location / {
proxy_pass http://172.31.36.28;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $http_x_real_ip;
}
}
|
同理,我们在Proxy_3
上也定义一个X-Real-IP
,并将获取到的上一级的X-Real-IP
赋值给自己的X-Real-IP
1
2
3
4
5
6
7
8
9
10
|
server {
listen 80;
server_name test.snoopyops.top;
location / {
proxy_pass http://172.31.36.28:8080;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $http_x_real_ip;
}
}
|
由于X-Real-IP
为自定义字段,Nginx
日志没有打印此字段,所以Nginx
日志没有发生变化。
Proxy_1
的Nginx
日志如下:
1
|
219.143.190.117 - - [03/Mar/2025:11:47:28 +0800] "GET / HTTP/1.1" 200 52 "-" "curl/8.4.0" "-"
|
Proxy_2
的Nginx
日志如下:
1
|
172.18.6.10 - - [03/Mar/2025:11:47:28 +0800] "GET / HTTP/1.0" 200 52 "-" "curl/8.4.0" "-"
|
Proxy_3
的Nginx
日志如下:
1
|
172.30.144.202 - - [03/Mar/2025:11:47:28 +0800] "GET / HTTP/1.0" 200 52 "-" "curl/8.4.0" "-"
|
但是我们后端服务已经可以通过X-Real-IP
获取到客户端的真实IP
了。服务端日志如下:
1
2
|
Server Started on :8080
Client IP: 219.143.190.117 | Method: GET | URL: /
|
X-Real-IP
是一个自定义的HTTP
头部字段,通常用于在代理服务器转发请求时,传递客户端的真实IP
地址。当使用代理时,代理服务器会将客户端的真实IP
地址填充到这个头部字段中,然后后端服务可以通过配置从这个头部获取客户端真实IP
。不过,它并非标准HTTP
头部,依赖于代理服务器是否正确设置,且如果代理链中有恶意节点,可能会被篡改。
X-Forwarded-For
X-Forwarded-For
是一个标准的HTTP
头部字段,用于记录请求经过的代理服务器列表。它的格式为client_ip, proxy1_ip, proxy2_ip...
,其中client_ip
是客户端的真实IP
,后面依次是请求经过的代理服务器IP
。
相比X-Real-IP
,X-Forwarded-For
更规范,被广泛支持。但由于它记录了整个代理链的IP
,在处理时需要解析地址,获取第一个字段。
我们需要在Nginx服务器上配置X-Forwarded-For
请求头。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
# proxy_1的配置
server {
listen 80;
server_name test.snoopyops.top;
location / {
proxy_pass http://172.30.144.202;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# proxy_2的配置
server {
listen 80;
server_name test.snoopyops.top;
location / {
proxy_pass http://172.31.36.28;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# proxy_3的配置
server {
listen 80;
server_name test.snoopyops.top;
location / {
proxy_pass http://172.31.36.28:8080;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
|
在Nginx
默认的日志格式中,最后一个字段表示的就是X-Forwarded-For
,各级代理日志如下,我们发现客户端真实IP
及各级代理IP
都会被记录。
1
2
3
4
5
6
7
8
|
# Proxy_1的Nginx日志
219.143.190.117 - - [03/Mar/2025:11:55:35 +0800] "GET / HTTP/1.1" 200 52 "-" "curl/8.4.0" "-"
# Proxy_2的Nginx日志
172.18.6.10 - - [03/Mar/2025:11:55:35 +0800] "GET / HTTP/1.0" 200 52 "-" "curl/8.4.0" "219.143.190.117"
# Proxy_3的Nginx日志
172.30.144.202 - - [03/Mar/2025:11:55:35 +0800] "GET / HTTP/1.0" 200 52 "-" "curl/8.4.0" "219.143.190.117, 172.18.6.10"
|
总结
字段 |
说明 |
remote_addr |
表示的是与Nginx服务器直接建立TCP连接的客户端的IP地址,多级代理下无法获取客户端真实IP。 |
X-Real-IP |
自定义的HTTP头部字段,可传递客户端的真实IP地址,依赖于代理服务器是否正确设置,有被篡改的风险。 |
X-Forwarded-For |
标准的HTTP头部字段,用于记录请求经过的代理服务器列表,但由于它记录了整个代理链的IP。在处理时需要解析地址,获取第一个字段。 |