以下内容主要来自HTTP 请求头中的 X-Forwarded-For | JerryQu 的博客↗、Difference between X-Forwarded-For and X-Real-IP headers – Stack Overflow↗和reverse proxy – nginx real_ip_header and X-Forwarded-For seems wrong – Server Fault↗。
X-Forwarded-For 是一个 HTTP 扩展头部。HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP。如今它已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入RFC 7239↗(Forwarded HTTP Extension)标准之中。
X-Forwarded-For 请求头格式非常简单,如下所示:
X-Forwarded-For: client, proxy1, proxy2
可以看到,XFF 的内容由「英文逗号 + 空格」隔开的多个部分组成,最开始的是离服务端最远的设备 IP,然后是每一级代理设备的 IP。
如果一个 HTTP 请求到达服务器之前,经过了三个代理 Proxy1、Proxy2、Proxy3,IP 分别为 IP1、IP2、IP3,用户真实 IP 为 IP0,那么按照 XFF 标准,服务端最终会收到以下信息:
X-Forwarded-For: IP0, IP1, IP2
在Nginx中,添加如下配置即可启用:
location /test {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
X-Real-IP 只是一个自定义头部字段,不属于任何标准,通常获取自$remote_add变量,也就是正在与服务器连接的客户端IP地址,可能是代理,也可能是真正的请求端。
由于请求头十分容易伪造,非可信服务器发来的 X-Real-IP 和 X-Forwarded-For 实际上完全不可相信,可靠的只有目前正在连接的IP,基于TCP的三次握手,此时不可能造假。
另外,以下配置其实是不合理的:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
这会导致请求到达 Nginx 之前的所有代理信息都被抹掉。
应只在最外层代理服务器内配置 X-Real-IP 为 $remote_addr 以获取可靠客户端地址,内层继续配置会导致正确地址被覆盖。
对 X-Forwarded-For 各层代理应逐级均配置为:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
使各个代理逐层添加自身IP。
另外,根据文心一言的回答:
在Nginx配置中使用proxy_set_header指令来设置向后端服务器传递的请求头时,X-Forwarded-For是一个常用的头部,用于标识客户端的原始IP地址,尤其是在使用了反向代理或负载均衡的场景中。你提到的三种设置方式在行为上有所不同,下面逐一解释:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
这是推荐的使用方式。$proxy_add_x_forwarded_for变量是Nginx特有的,用于自动地在已存在的X-Forwarded-For头部后追加客户端的IP地址,如果请求中原本没有X-Forwarded-For头部,则直接使用客户端的IP地址。这种方式能够确保在多级代理的情况下,客户端的原始IP地址被正确地传递,并且不会丢失之前代理的IP信息。
proxy_set_header X-Forwarded-For $http_x_forwarded_for;
这种方式直接从HTTP请求中获取X-Forwarded-For头部的值,并将其传递给后端服务器。如果请求中没有X-Forwarded-For头部,则不会设置这个头部。这种方式的缺点在于,如果请求在到达Nginx之前没有被正确地设置X-Forwarded-For(比如,某些客户端或代理服务器可能不设置这个头部),则后端服务器将无法获取到客户端的真实IP地址。此外,这种方式在多级代理的场景中可能会丢失一些代理的IP信息,因为它不会追加新的IP地址到X-Forwarded-For头部。
proxy_set_header X-Forwarded-For $remote_addr;
这种方式直接将Nginx接收到的请求来源IP地址(即直接连接到Nginx的客户端的IP地址)设置为X-Forwarded-For的值。这种方式适用于简单的代理场景,比如只有一层Nginx作为反向代理时。然而,在多层代理的场景下,它无法传递出客户端的原始IP地址,因为$remote_addr只包含直接连接到Nginx的客户端的IP地址,而不包括之前代理的IP地址。
综上所述,推荐使用$proxy_add_x_forwarded_for来设置X-Forwarded-For头部,因为它能够确保在多级代理的场景下,客户端的原始IP地址被完整地传递,并且不会丢失之前代理的IP信息。