通过阿里云 ALB 获取客户端真实IP 指南

📝 前言

应用型负载均衡ALB(Application Load Balancer)是阿里云推出的专门面向HTTP、HTTPS和QUIC等应用层负载场景的负载均衡服务,具备超强弹性及大规模应用层流量处理能力。

在互联网架构中,为了实现弹性伸缩、容灾能力和服务高可用,很多系统会部署在负载均衡器(如 ALB)之后。

🧭 背景介绍

在上一次的网站架构调整中,本站加入了阿里云ALB组件,详情可参看这篇文章介绍《我的网站架构演进之路(从单机→云原生)》,所有的流量都先从 ESA(CDN) 走到 ALB,最后到各个服务,但是近日我发现了一个问题。

事情起因是有一位博友在评论区问我网站流量咋样,平均每天能有多少ip访问,我才去看了眼访客数据,发现这个数值很久没有变动了,但是实际情况肯定不是这样的,每天多少是有些新IP访问进来的,比如爬虫等:)。

经过排查才发现是 ESA 未将真实客户端IP 传递到后端导致的,这里就记录一下排查和解决过程。

PS:这还得怪我平常是真的没太在意网站的统计数据,过了大半个月才发现。。。

🚀 详细步骤

1️⃣ 阿里云 ESA(CDN)隐藏了真实客户端IP

通过查阅阿里云官方文档《NGINX获取客户端真实IP》,可以发现默认 ESA 会隐藏客户端真实IP,需要开启四层代理才能获取,但是四层代理又是企业版专属功能。

我买的是基础版肯定是用不了的,作为一个小站也没必要去开通企业版,因此只能去掉统计组件的ESA 加速。

PS:这里不止是统计组件,还有评论组件也依赖真实客户端IP 用于展示访客地理位置,也需要一并去除。

另外看下了腾讯云EdgeOne 也是同理,四层代理是属于企业版功能,如果使用的腾讯云加速最好也去掉。

2️⃣ 配置 ALB 代理

去掉了 ESA 加速后,如果直接将统计域名解析到服务器公网IP,就可以获取到真实客户端IP。

但是这样一来是不够安全,二来也不够灵活,再加上我们本来就有购买 ALB 服务,因此最好还是通过 ALB 进行代理。

ALB 默认就是有开启客户端真实IP 转发配置,进入实例详情 → 监听详情可查看,如下图:

至于 ALB 配置转发到 ECS 就比较简单了,创建服务器组 → 添加后端ECS服务器 → 创建监听 → 配置转发规则就这固定流程,这里就不再展开赘述了。

3️⃣ 配置 Nginx 代理

既然 ALB 默认已经支持转发客户端真实IP了,那我们在后端的 Nginx 如何配置获取呢。

在日志打印中我们可以添加以下配置:

1
2
3
log_format  main  '$http_x_forwarded_for - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

其中比较关键的就是http_x_forwarded_for,这个就是ALB传递过来的客户端真实IP。

然后我们还需要在http或server段中添加以下配置解析这个IP:

1
2
3
real_ip_header X-Forwarded-For;
set_real_ip_from 0.0.0.0/0; # ALB的VPC IP网段,可根据实际设置
real_ip_recursive on;

参数说明:

  • real_ip_header: 是 Nginx 中用于指定包含客户端真实 IP 地址的 HTTP 请求头字段的配置指令,通常需要与 set_real_ip_from 指令配合使用。
  • set_real_ip_from:定义可信的代理服务器 IP 范围(即允许修改 X-Forwarded-For 的代理)。只有来自这些 IP 的请求,Nginx 才会处理其 X-Forwarded-For 头部,避免伪造 IP 的风险,建议不要直接使用0.0.0.0/0,可以根据实际设置。
  • real_ip_recursive: 开启递归查找,从 X-Forwarded-For 的右侧开始向左查找第一个非可信代理的 IP 作为客户端真实 IP。

最后就是正常的location代理配置了,参考如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
location / {
# 保证获取到真实IP
proxy_set_header X-Real-IP $remote_addr;
# 真实端口号
proxy_set_header X-Real-Port $remote_port;
# X-Forwarded-For 是一个 HTTP 扩展头部。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 在多级代理的情况下,记录每次代理之前的客户端真实ip
proxy_set_header HTTP_X_FORWARDED_FOR $remote_addr;
# 获取到真实协议
proxy_set_header X-Forwarded-Proto $scheme;
# 真实主机名
proxy_set_header Host $host;
# 设置变量
proxy_set_header X-NginX-Proxy true;
# 开启 brotli
proxy_set_header Accept-Encoding "";

# 后端服务地址
proxy_pass http://127.0.0.1:5003;
proxy_redirect off;
}

✅ 总结

总结一下,如果后端服务依赖客户端真实IP,就不要使用 CDN 加速了,可以通过 ALB 负载均衡代理也可以直接解析到 ECS 服务器。

如果是通过 ALB 代理,主要依赖于X-Forwarded-For头部的正确传递与解析。