ALB 日志里的 host 为什么不是负载均衡公网 IP

ALB 日志里的 host 为什么不是负载均衡公网 IP

线上排查 Web 扫描日志时,经常会看到一个容易误判的现象:请求明明是从负载均衡公网 IP 进来的,但 ALB 或 Nginx 日志里的 host 却是另一个陌生 IP。

结论先说清楚:日志里的 host 记录的是 HTTP 请求头里的 Host,不是客户端真正连接的公网入口 IP。 客户端可以连接 203.0.113.10:80,同时在 HTTP 层写 Host: 192.0.2.157:8826。如果 ALB 没有针对 Host 做拒绝规则,这个请求会走默认转发规则,后端 Nginx 也会把这个 Host 原样记下来。

本文用一个脱敏例子说明这个过程。

环境

项目 示例
云产品 阿里云 ALB 七层负载均衡
日志 ALB 访问日志投递到 SLS,后端 Nginx access log 也投递到 SLS
协议 HTTP/1.1
公网入口 203.0.113.10:80,示例地址
客户端 IP 198.51.100.23,示例地址
客户端自定义 Host 192.0.2.157:8826,示例地址
后端 Nginx 10.0.1.11:80,示例地址

文中的 IP 使用 RFC 5737 文档地址段和私网地址做脱敏示例,不对应真实生产资源。

请求链路

动态看一遍就是这样:

链路可以拆成五步:

  1. 客户端 TCP 连接的是 ALB 公网入口 203.0.113.10:80
  2. 客户端在 HTTP 请求里带了 Host: 192.0.2.157:8826
  3. ALB 收到请求,访问日志记录 host=192.0.2.157http_host=192.0.2.157:8826
  4. 如果没有更精确的 Host 路由命中,请求走默认规则转发到后端。
  5. 后端 Nginx 继续把请求头里的 Host 记到 access log。

所以不要把 host 当成“入口 IP”。入口要看 ALB 自己的字段,比如 app_lb_idvip_addrslb_vport,以及云资产里这个 vip_addr 对应的公网 EIP。

模拟一次这种请求

curl 就可以复现。关键点是:URL 里写实际访问的入口 IP,Header 里写另一个 Host。

curl -i \
  -H 'Host: 192.0.2.157:8826' \
  -H 'User-Agent: host-header-probe/1.0' \
  http://203.0.113.10/__host_header_probe

这个请求的含义不是“访问 192.0.2.157”,而是:

TCP 连接: 203.0.113.10:80
HTTP Host: 192.0.2.157:8826
URI: /__host_header_probe

如果后端没有这个路径,一般会返回 404。这不影响验证,因为我们要看的不是业务响应,而是 ALB 和 Nginx 日志怎样记录字段。

ALB 日志里会看到什么

ALB 七层访问日志里常见字段类似这样:

app_lb_id: alb-xxxxxxxx
alb_rule_id: rule-default-lsn-xxxxxxxx
client_ip: 198.51.100.23
client_port: 57198
host: 192.0.2.157
http_host: 192.0.2.157:8826
request_method: GET
request_uri: /__host_header_probe
scheme: http
slb_vport: 80
vip_addr: 10.0.0.10
upstream_addr: 10.0.1.11:80
status: 404
http_user_agent: host-header-probe/1.0

几个字段要分开看:

字段 含义
client_ip 连接到 ALB 的客户端 IP
host / http_host HTTP 请求头里的 Host,由客户端传入
app_lb_id 命中的 ALB 实例
alb_rule_id 命中的 ALB 转发规则,rule-default-* 表示默认规则
slb_vport ALB 监听端口
vip_addr ALB 的内网 VIP
upstream_addr ALB 转发到的后端地址

真正判断“从哪个负载均衡入口进来”,不要看 host,要看:

app_lb_id + vip_addr + slb_vport

再结合云资产里 ALB 的 ZoneMappings / LoadBalancerAddresses,把 vip_addr 映射回公网 EIP。

为什么会走默认规则

示例里的 alb_rule_id 是:

rule-default-lsn-xxxxxxxx

这说明没有命中更具体的 Host / Path 转发规则,ALB 用监听器默认动作把请求转发到了默认后端。

这类请求常见于扫描器:

GET /.env HTTP/1.1
Host: 192.0.2.157:8826
User-Agent: go-scanner/0.1

扫描器不一定知道真实域名。它可能随机拿一个 IP 当 Host,也可能从历史资产库里取一个值,然后探测 /.env/.git/config/.aws/credentials 之类敏感路径。

只要公网入口接受这个请求,并且默认规则能转发到后端,后端日志就会出现这个“看起来不是自己资产”的 Host。

Nginx 日志为什么也会记录这个 Host

Nginx 看到的是 ALB 转发过来的 HTTP 请求。请求头里的 Host 没变,所以 Nginx access log 里如果记录 $host$http_host,就会继续看到:

host: 192.0.2.157
http_host: 192.0.2.157:8826

同时,Nginx 侧通常还能看到这些字段:

remote_addr: 10.0.0.126
server_addr: 10.0.1.11
http_x_forwarded_for: 198.51.100.23
upstream_addr: 127.0.0.1:8080

这里的含义是:

字段 含义
remote_addr Nginx 看到的上一跳,一般是 ALB 本地转发地址
server_addr 处理请求的 Nginx 机器地址
http_x_forwarded_for ALB 传给后端的真实客户端 IP
upstream_addr Nginx 再转发到的业务进程

所以 Nginx 日志可以反推链路,但如果要精确到 ALB 公网入口,最好查 ALB 原始访问日志。

SLS 查询方法

先取一条样例日志,确认字段名:

aliyun sls get-logs-v2 \
  --project=<project> \
  --logstore=<logstore> \
  --endpoint=<region>.log.aliyuncs.com \
  --from <start_ts> \
  --to <end_ts> \
  --query '*' \
  --line 1 \
  --format-output json

确认字段后,再按 Host 和 URI 查:

aliyun sls get-logs-v2 \
  --project=<project> \
  --logstore=<logstore> \
  --endpoint=<region>.log.aliyuncs.com \
  --from <start_ts> \
  --to <end_ts> \
  --query 'http_host: 192.0.2.157 and request_uri: "/.env"' \
  --line 20 \
  --format-output json

如果想按真实客户端 IP 查,ALB 日志里优先看 client_ip

aliyun sls get-logs-v2 \
  --project=<project> \
  --logstore=<logstore> \
  --endpoint=<region>.log.aliyuncs.com \
  --from <start_ts> \
  --to <end_ts> \
  --query 'client_ip: 198.51.100.23 and request_uri: "/.env"' \
  --line 20 \
  --format-output json

注意:ALB 日志里的 http_x_forwarded_for 可能是 -,因为客户端直连 ALB 时本来没有带这个头。到 Nginx 后,ALB 可能会把 client_ip 写入 X-Forwarded-For,所以 Nginx 日志里又能看到 http_x_forwarded_for=198.51.100.23

排查时怎么判断入口

建议按这个顺序看:

  1. 从 Nginx 日志拿到 remote_addrserver_addrhttp_x_forwarded_forhostrequest_uri
  2. 用同一时间窗口查 ALB SLS。
  3. 在 ALB 日志里找到同一条 request_urihost/http_hostclient_ip
  4. app_lb_id 确认负载均衡实例。
  5. vip_addrslb_vport 确认入口监听。
  6. 用 ALB 资产里的 ZoneMappings / LoadBalancerAddressesvip_addr 映射到公网 EIP。
  7. upstream_addr 确认后端机器。

一个完整链路最终会长这样:

198.51.100.23
 -> 203.0.113.10:80
 -> ALB vip 10.0.0.10
 -> 10.0.1.11:80 Nginx
 -> 127.0.0.1:8080

而日志里的 Host 可能仍然是:

192.0.2.157:8826

这两个并不冲突。

如何减少这类噪音

如果业务不需要接受任意 Host,可以考虑做几件事:

  1. 在 ALB 上增加明确的 Host 转发规则,只让真实域名进入业务后端。
  2. 默认规则转发到一个固定的 404 后端,不进入主业务服务。
  3. 在 Nginx 默认 server 里拒绝未知 Host。
  4. WAF / 安全事件系统里把“访问入口”和“HTTP Host”拆成两个字段,避免把 Host 误当成域名资产。
  5. /.env/.git/config 等敏感路径做单独统计,避免一次扫描就误判成真实业务异常。

Nginx 默认拒绝未知 Host 的思路大概是:

server {
    listen 80 default_server;
    server_name _;
    return 444;
}

实际生产环境要先确认健康检查、默认域名、回源链路和 CDN 行为,不要直接复制上线。

快速参考

字段速查:

要判断的问题 优先看哪个字段
客户端从哪里来 ALB client_ip,Nginx http_x_forwarded_for
客户端写了什么 Host ALB host/http_host,Nginx $host/$http_host
命中哪个 ALB app_lb_id
命中哪个入口监听 vip_addr + slb_vport
是否走默认规则 alb_rule_id 是否是 rule-default-*
转发到哪个后端 ALB upstream_addr,Nginx server_addr

模拟命令:

curl -i \
  -H 'Host: 192.0.2.157:8826' \
  -H 'User-Agent: host-header-probe/1.0' \
  http://203.0.113.10/__host_header_probe

核心结论:

URL 里的 IP = 实际 TCP 连接入口
Host 头 = HTTP 层客户端声明的目标主机
ALB/Nginx 日志里的 host = Host 头,不等于入口 IP

看到陌生 IP 出现在 host 字段里,先不要判断为“域名展示错了”或“资产漏同步”。先查 ALB 原始日志里的 app_lb_idvip_addrclient_ipupstream_addr,再还原真实入口链路。

posted @ 2026-07-27 14:56  Hello_worlds  阅读(1)  评论(0)    收藏  举报