nginx获取用户真实ip
nginx日志里全是获户127.0.0.1,排查用户问题像在猜谜。取用这场景干过运维的获户都懂。
你的取用服务跑在nginx后面,PHP或Node拿到的获户IP永远是代理服务器的。用户骂你地域歧视,取用后台数据显示他们全来自本地机房。获户别慌,取用问题不在代码逻辑,获户是取用nginx没把真实IP传下去。
最典型的获户场景是没做任何处理时,`$remote_addr`就是取用跟你握手的那个IP。如果架构是获户“用户→nginx→后端”,那nginx看到的取用确实是用户IP,但后端看到的获户却是nginx的IP。可一旦中间多了层负载均衡或CDN,连nginx看到的都不对了。
解决方案分两层。第一层,nginx得知道谁在跟它直连。假设你的架构是“用户→CDN→nginx”,那nginx的直连方是CDN节点,不是用户。这时需要用`set_real_ip_from`告诉nginx:“这些IP段来的请求,别信它们的来源,去HTTP头里找真身。”配合`real_ip_header`指定从哪个头取,通常是`X-Forwarded-For`。
配置长这样:
set_real_ip_from 103.21.244.0/22; set_real_ip_from 103.22.200.0/22; real_ip_header X-Forwarded-For; real_ip_recursive on; 最后那行`real_ip_recursive on`是关键。X-Forwarded-For是个追加列表,客户端IP在最左,每经过一跳就往右加一个。不加recursive,nginx只取最右那个(也就是CDN自己的IP),加了才从左往右找第一个非信任IP。这个细节坑过不少人,我见过线上配完还是全显示CDN节点IP的,就是漏了这行。
第二层,把IP往后端传。nginx拿到真实IP后,默认会把它放进`X-Real-IP`头(如果编译了`http_realip_module`)。后端程序读这个头就行。但有些框架更习惯读`X-Forwarded-For`,那就得手动改:
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 注意`$proxy_add_x_forwarded_for`会在原有列表基础上追加`$remote_addr`,而此时的`$remote_addr`已经是修正后的真实IP了。
有个容易忽略的点:如果CDN厂商没把用户IP写进XFF,你配再多规则也白搭。阿里云CDN默认会带,但有些小厂商只给`X-Forwarded-Proto`不给IP。上线前用`curl -H "X-Forwarded-For: 1.2.3.4"`模拟请求测一下,看后端能不能读到1.2.3.4。
另外,别信`X-Real-IP`一定安全。如果nginx直接暴露公网,任何人都能伪造这个头。所以`set_real_ip_from`必须只填你信任的代理IP段,别图省事写`0.0.0.0/0`。那不是解决问题,是把安全门拆了。
日志里看到IP对了也别急着庆祝。检查一下`$http_cf_connecting_ip`——如果你用了Cloudflare,它有自己的头。CF的请求会覆盖XFF,你得在`set_real_ip_from`里加上CF的IP段,并把`real_ip_header`改成`CF-Connecting-IP`。这俩头选一个用,别混着配。
最后说个查问题的笨办法:在nginx配置里加个自定义日志格式,把`$remote_addr`、`$http_x_forwarded_for`、`$http_cf_connecting_ip`全打出来,看三者的差异。差异在哪,问题就在哪。我见过最离谱的一次是后端代码里又套了一层代理,nginx配得完美,但应用自己发了内部请求,日志里全是内网IP。那已经不是nginx的锅了。