一、概述
X-Forwarded-For, X-Real-IP, remote_addr 是 http 协议中用来表示客户端地址的请求头。
X-Forwarded-For 和 X-Real-IP 只有请求存在代理时才有值,而 remote_addr 一直存在。
X-Forwarded-For:记录代理服务器的地址,每经过一个代理,该字段会加上一个记录。格式形如:1.1.1.1, 2.2.2.2X-Real-IP:也是用来记录服务器的地址,但是和上面的不同,它不把记录添加到结尾,而是直接替换remote_addr:上一个客户端连接的地址,不存在代理就表示客户端的地址,存在代理就表示最后一个代理服务器的地址
以下使用三个 nginx 服务器来测试以上三个请求头在不同条件组合下的值:
本机地址:
192.168.234.1。server1: https://192.168.234.13
server2: https://192.168.234.128
server3: https://test.vazd.xyz
server1 的代理配置
|
1 2 3 4 5 6 7 8 9 |
server { listen 80; server_name _; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass https://192.168.234.131; } } |
server2 的代理配置
|
1 2 3 4 5 6 7 8 9 |
server { listen 80; server_name _; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass https://test.vazd.xyz; } } |
server3 为 web 服务端
|
1 2 3 4 5 6 7 8 9 |
server{ listen 80; server_name test.vazd.xyz; location / { root /data/html; index index.html; } access_log /data/wwwlogs/test.vazd.xyz/access.log test; } |
日志格式 test 分别打印出三个请求头的内容:
|
1 |
log_format test "$http_x_forwarded_for | $http_x_real_ip | $remote_addr"; |
在不使用代理直接访问 https://test.vazd.xyz 的情况下日志信息为:
|
1 |
- | - | 223.73.59.254 |
因为不存在代理,所以前面两个值是不存在的。而 server3 是公网地址,记录的 remote_addr 是本机公网地址 223.73.59.254 。
二、请求说明
X-Forwarded-For 就像是 vector 中的 push_back,每经过一个代理服务器就把该请求的来源地址加在记录的后面 (由于是记录来源地址,所以该字段不会保存最后一个代理服务器的地址) 。
在本机访问 server1,请求会被转发到 server3,此时 server3 的日志为:
|
1 |
192.168.234.1, 192.168.234.128 | 192.168.234.128 | 223.73.59.254 |
X-Forwarded-For 为 192.168.234.1, 192.168.234.128,说明经过了两次转发,但并没有记录 server2 的地址。
X-Real-IP 会替换代理服务器的地址,请求头会在经过 server2 时被设置位 server1 的地址。
三、不记录转发请求
如果把 server2 的 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 注释掉,即 server2 在转发时不添加该请求头,X-Forwarded-For 就不会加上 server1 的地址:
|
1 |
192.168.234.1 | 192.168.234.128 | 223.73.59.254 |
如果再把 server2 的 proxy_set_header X-Real-IP $remote_addr; 也注释掉,请求在 server2 时也不会替换 X-Real-IP 的值。
|
1 |
192.168.234.1 | 192.168.234.1 | 223.73.59.254 |
但是不管怎样,remote_addr 是永远不会变的,它永远都是上一个请求客户端的地址,也就是 server2 的地址。
四、伪造请求头
请求头是可以伪造的,如果客户端伪造了请求头地址,X-Forwarded-For 和 X-Real-IP 将会收到影响。
|
1 2 |
# server2 上请求服务,并手动构造请求头 > curl 127.0.0.1 -H "X-Forwarded-For: 1.1.1.1" -H "X-Real-IP: 2.2.2.2" |
收到的日志信息:
|
1 |
1.1.1.1 | 2.2.2.2 | 223.73.59.254 |
tips:对于 X-Real-IP 来说,如果存在一级以上的代理,它也不会收到影响,因为每经过一次代理,它就会被覆盖
而 remote_addr 这个地址不管怎样伪造都是无用的:
|
1 2 3 4 |
# 在 server2 上直接请求 server3 > curl https://test.vazd.xyz -H "remote_addr:1.1.1.1" # 得到的日志信息 - | - | 223.73.59.254 |
因为 remote_addr 字段不是通过请求头来决定的,而是服务端在建立 tcp 连接时获取的的客户端地址。















1F
X-Real-IP 在客户端直连 WEB 端的时候可以被伪造,但是中间如果经过了一层 nginx 代理伪造就没用了。