关于nginx - $remote_addr的解释

nginx的自带变量 $remote_addr 代表客户端的IP

remote_addr代表客户端的IP,但它的值不是由客户端提供的,而是服务端根据客户端的ip指定的.

当你的浏览器访问某个网站时,假设中间没有任何代理,那么网站的web服务器(Nginx,Apache等)就会把remote_addr设为你的机器IP,如果你用了某个代理(比如clb),那么你的浏览器会先访问这个代理,然后再由这个代理转发到网站,这样web服务器就会把remote_addr设为这台代理机器的IP。

但是实际场景中,我们即使有代理,也需要将$remote_addr设置为真实的用户IP, 以下利用with-http_realip_module模块实现

安装(nginx或者openresty编译需要加--with-http_realip_module)

1
./configure ...... --with-http_realip_module

添加如下配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 从哪个header头检索出要的IP地址
real_ip_header X-Forwarded-For;
real_ip_recursive on;
# set_real_ip_from 172.17.10.125; #上一层代理IP地址
set_real_ip_from 0.0.0.0/0;
# 设置为ip段
#set_real_ip_from 192.168.50.0/24;
#set_real_ip_from 61.22.22.22;
#set_real_ip_from 121.207.33.33;
#set_real_ip_from 127.0.0.1;

# 下面三行为常见反向代理传递真实客户端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;
  • 字段说明

set_real_ip_from为定义信任ip,如果发送方的ip不在信任中,则不处理,$remote_addr就还会是发送发的地址。如果ip在信任中,则会根据下一个参数real_ip_header的设定,将指定的header头字段的数据作为$remote_addr。

如果real_ip_header中定义的header头字段,只传来了一个IP,比如默认的X-Real-Ip,则会直接将这个ip作为$remote_addr。这时候,如果像阿里云SLB,CDN,或者别的nginx等这类代理传来的,其真实IP可以是通过别的header字段传来的,那么跟去实际情况,定义此字段,这里我用的常见的X-Forwarded-For字段

这里需要说明一下X-Forwarded-For这个header信息,用于记录此请求所进过的ip,假设本nginx为第3层代理,那么获取到的X-Forwarded-For就会记录3个ip,分别顺序为:

1
用户IP 第一层代理IP 第二层代理IP

默认情况下x-forwarded-for是个串

假设用户的ip为: 120.22.11.11

  • 在real_ip_recursive on的情况下

    1. 假如 61.22.22.22, 121.207.33.33, 192.168.50.121 都出现在set_real_ip_from中,仅仅120.22.11.11没出现,那么他就被认为是用户的ip地址,并且赋值到remote_addr变量
    2. 总结: 从右边往左一直取到第一个不信任的IP作为$remote_addr,示例这里是全部信任,那么最左边的IP则会被作为$remote_addr。
  • 在real_ip_recursive off或者不设置的情况下

    1. 假如192.168.50.121出现在set_real_ip_from中,排除掉,接下来的ip地址便认为是用户的ip地址

    2. 总结: 从右往左,取第一个出现在信任中的IP的左边一位的IP作为$remote_addr,示例这里是全信任,所以就会取到第一层代理IP