在引入前置代理、CDN 或四层负载均衡之后,网关收到的客户端地址往往变成了代理服务器的 IP,导致基于用户真实 IP 的限速、访问控制和审计日志全部失效。本文系统梳理 OpenResty Edge 支持的四种真实 IP 还原方案:Proxy Protocol、X-Forwarded-For、X-Real-IP 和自定义头。涵盖各方案的适用场景、配置步骤与安全注意事项,并额外说明如何在回源时将真实客户端 IP 同步传递至后端源站,形成完整的入站与出站双向闭环。您能够根据自身代理拓扑选择合适方案,并在 OpenResty Edge 上完成端到端配置。

1. 使用场景与拓扑图

当请求经过第三方代理转发时,OpenResty Edge 收到的客户端地址是代理服务器的地址,而非真实的终端用户 IP。

真实客户端 (104.28.243.40)
        │
        ▼
前置代理服务器 (52.53.251.226)
        │  携带真实 IP 信息(via XFF / Real-IP / Proxy Protocol)
        ▼
OpenResty Edge 网关
        │  还原 client-addr → 用于访问控制 / 限速 / 日志
        ▼
后端服务 / 源站

需要还原真实客户端 IP 的典型场景:

  • 条件判断:基于用户真实 IP 的地理位置匹配、黑白名单
  • 请求限速:对单个真实用户而非代理 IP 进行频率控制
  • 日志记录:审计日志中记录真实来源地址

OpenResty Edge 支持四种 IP 传递方案,适用于不同的代理拓扑和协议层。

2. IP 传递方案概览与适用场景

方案传递层Stream 可用HTTP 可用多级代理
proxy protocolTCP/UDP/TLS 链接头部数据
X-Forwarded-ForHTTP 头
X-Real-IPHTTP 头
自定义头HTTP 头取决于实现

Stream 场景约束:TCP/UDP 四层代理中不存在 HTTP 头,因此所有基于 HTTP 头的方案均不可用。在本文所讨论的方案中,proxy protocol 是 Stream 场景下最主流的选择;此外,TOA(TCP Option Address)通过将客户端 IP 写入 TCP 选项字段来透传真实地址,同样适用于四层代理场景,但其使用依赖内核模块支持,兼容性和部署成本与 proxy protocol 存在差异,实际选型需结合具体环境评估。

2.1 proxy protocol V1

核心机制:前置代理在建立 TCP 连接时,于数据流最前端注入一行标准化的 PROXY 头(以下以 Proxy Protocol v1 格式为例)

PROXY TCP4 <真实客户端 IP> <代理服务器 IP> <源端口> <目标端口>\r\n

网关在 TCP 层解析该头部,提取真实 IP 写入 proxy_protocol_addr,进而更新 client-addr

适用场景:四层 TCP/UDP Stream 代理;HTTP 场景下上游已配置发送 proxy protocol 头。

限制条件

  • 前置代理必须主动发送合规的 proxy protocol V1 头
  • OpenResty Edge 对应端口须提前启用 proxy protocol 支持(见 3.1 节)
  • 必须把前置代理服务器添加到可信的 IP 列表中
  • OpenResty Edge 在同一个端口可以支持多种不同方案的源 IP 传递方案。

配置步骤:先在端口级别启用(3.1 节),再在全局配置中选择 Proxy Protocol 来源(3.2 节)。

2.2 Real-IP

核心机制:前置代理将真实客户端 IP 写入 X-Real-IP HTTP 头,由网关从中提取并更新 client-addr

适用场景:前置代理负责将最终用户的真实 IP 统一写入该头、且无需在网关侧还原完整代理链路的场景。上游代理可控,写入逻辑明确即可,不限于单跳拓扑。

限制条件

  • X-Real-IP 无标准规范,不同上游代理的实现行为存在差异:多数实现写入单个 IP,部分实现(如将其作为 X-Forwarded-For 别名)可能写入逗号分隔的多值;OpenResty Edge 的处理行为取决于所配置的解析方式,使用前应确认上游写入格式。
  • 该头不具备逐跳追加语义,链路中间节点的 IP 不会被自动记录,若需还原完整的多级代理链路,应改用 X-Forwarded-For。
  • 仅适用于 HTTP/HTTPS 场景。

2.3 X-Forwarded-For

核心机制:每经过一个代理节点,该节点将入站 IP 追加至 X-Forwarded-For 头末尾,形成逗号分隔的 IP 列表。

未启用递归搜索时OpenResty Edge 取 XFF 列表中最右侧(最后添加)的 IP 作为客户端地址。

启用递归搜索 IP 后:系统从右向左遍历 XFF 列表,跳过所有在可信来源列表中的地址,取第一个非可信地址作为真实客户端 IP,可有效抵御头部伪造。

适用场景:多级 HTTP 代理链路;与主流代理(Nginx、HAProxy、CDN 节点)兼容。

限制条件:头部可被客户端伪造,必须严格配置可信来源 IP(见 3.3 节)。

2.4 自定义头

核心机制:由运维人员指定任意 HTTP 头字段名称作为真实 IP 来源,OpenResty Edge 从该头中提取客户端 IP。

适用场景:内部系统已有私有 IP 传递规范(如 CF-Connecting-IPTrue-Client-IP 等);无需修改前置代理的头部字段名。

限制条件:前置代理须配合写入指定头字段;仅适用于 HTTP/HTTPS。

3. OpenResty Edge 配置

3.1 网关分区端口启用 proxy-protocol 支持

使用 proxy protocol 方案时,必须先在端口级别开启接收支持,其他方案可跳过此步骤。

  1. 进入 Admin Web 控制台,导航至 网关集群 > 网关分区
  2. 选择目标分区,点击右侧 编辑 按钮
  3. 在端口列表中找到目标端口,点击右侧 编辑 按钮
  4. 勾选 启用 proxy protocol,保存配置
  5. 在应用配置中选择想要下发的分区

预留端口,请勿将以下端口用于业务配置:80443(默认代理,公网);11212(共享 SSL Session,内网);8090(集群资源共享,内网);8091(节点状态查询,仅本地)。

3.2 全局配置启用相关协议

  1. 进入 Global Config 页面
  2. 客户端 IP 来源 中选择实际使用的方案:
选项说明
X-Forwarded-For从 XFF HTTP 头获取真实客户端 IP 和端口
Proxy Protocol从 Proxy Protocol 协议头获取(须完成 3.1 节端口配置)
X-Real-IPX-Real-IP HTTP 头获取真实客户端 IP 和端口
Custom Header从自定义 HTTP 头字段获取,需同时填写头字段名称
  1. (可选)开启 递归搜索 IP:当头字段存在多个 IP 时,从后向前查找第一个非可信地址作为真实来源 IP,适合多级代理场景。
  2. 保存后点击 发布,配置将推送至所有网关服务器,无需重载或重启

3.3 配置可信来源 IP

Trusted hosts to set real IP 定义了哪些上游地址发来的 Real IP 头是可信的。只有当 TCP 连接的对端 IP 在此列表中时,OpenResty Edge 才会采信请求头中的真实 IP 并更新 client-addr;来自不可信地址的请求,其 Real IP 头将被忽略。

  1. Global Config 页面找到 Trusted hosts to set real IP
  2. 输入前置代理服务器的 IP 地址(示例:52.53.251.226),可添加多条
  3. 保存并发布

行为对比

请求来源携带头client-addr 结果
52.53.251.226(可信)X-Forwarded-For: 104.28.243.40104.28.243.40
不可信主机X-Forwarded-For: 104.28.243.40不可信主机的实际 IP

注意:真实来源 IP 配置生效后会影响所有依赖客户端 IP 的功能,包括 Client City、Client Address 显示、Limit Request Rate 动作等。例外:限制 TLS/SSL 握手速率不受影响,因握手阶段发生在 HTTP 层解析之前,此时 client-addr 尚未完成重写。

3.4 同端口多协议支持说明(对比 Nginx 原生限制)

Nginx 原生行为proxy_protocol 在 Nginx 中是 listen 指令的端口级开关:

# Nginx:同端口只能选择其一
listen 80 proxy_protocol;   # 该端口只接受带 proxy protocol 的连接
listen 80;                  # 该端口只接受普通连接

若上游混合发送(部分携带 proxy protocol 头,部分不携带),必须拆分监听端口分别处理,增加了端口管理和防火墙规则的复杂度。

OpenResty Edge 的处理方式:开启端口级 proxy protocol 支持后,网关能自动识别连接开头是否为合法的 PROXY 协议头,并据此选择解析路径,同一端口上可同时处理两类连接:

  • 连接以 PROXY TCP4 ... 开头 → 解析 proxy protocol 头,提取真实 IP
  • 连接为普通 HTTP/TCP 流量 → 按常规路径处理,不要求携带协议头

这一差异化能力在新旧代理混合迁移期间尤为实用,运维团队无需为不同上游维护不同监听端口。

4. 关键变量说明

4.1 client-addr

client-addrOpenResty Edge最终生效的客户端地址,贯穿所有与客户端 IP 相关的处理逻辑。

取值逻辑

  1. 初始值为 TCP 连接的对端 IP(即前置代理地址)
  2. 若该 IP 在可信来源列表中,且请求携带有效的 Real IP 头(或 proxy protocol 头),则 client-addr 被更新为解析出的真实 IP
  3. 若对端 IP 不在可信列表中,client-addr 保持 TCP 连接来源 IP 不变

影响范围

  • 页面规则条件判断(Client City、Client Address 等变量)
  • Limit Request Rate 限速动作(按真实用户 IP 限速)
  • 访问日志中的客户端地址字段
  • EdgeLang 中引用 client-addr 的所有规则

不受影响:TLS/SSL 握手速率限制。握手发生在 HTTP 解析之前,此时 client-addr 尚未完成重写,使用的仍是 TCP 连接来源 IP。

4.2 proxy_protocol_addr

proxy_protocol_addr 是从 Proxy Protocol 头中原始提取的客户端 IP,不经过可信来源规则的过滤。

变量数据来源经过信任规则典型用途
proxy_protocol_addrProxy Protocol 头原始值审计、调试
client-addr综合信任规则后的生效值业务逻辑、限速、日志

当来源 IP 通过可信验证时,两者值相同。当来源 IP 不可信时,proxy_protocol_addr 仍保留协议头中的解析值,而 client-addr 不会被更新,仍为 TCP 连接来源 IP。在需要同时记录原始协议头值和最终生效 IP 的审计场景中,可分别引用两个变量。

5. 页面规则

本节配置一条 EdgeLang 页面规则,使请求响应中直接返回网关识别到的 client-addr,作为验证各 IP 传递方案是否生效的基础。

操作步骤

  1. 进入目标应用(示例:test-edge.com),选择 Page Rules
  2. 点击 Edit(或新建规则)
  3. 使用 EdgeLang 编写规则:
# 条件:true(对所有请求生效)
# 动作:在响应体中输出当前 client-addr 的值
true =>
    say(client-addr),
    done;
  1. 保存并发布,配置推送至所有网关服务器,无需重启

发布后,对该域名的任何请求都将在响应中返回 OpenResty Edge 当前识别到的客户端地址,可直接用于验证 IP 传递是否正确。

6. 验证方案

以下验证均假设目标域名为 test-edge.com,且已按第 5 章完成页面规则配置。

6.1 curl 携带 X-Forwarded-For

前提:Global Config 已选择 X-Forwarded-For 来源,可信来源 IP 包含发出请求的代理服务器地址。

单 IP 场景:

curl http://test-edge.com/ -H "X-Forwarded-For: 104.28.243.40"
# 预期输出:104.28.243.40
# 说明:请求来自可信代理,XFF 头中的 IP 被采信,client-addr 更新为 104.28.243.40

多 IP 场景(取最后一个):

curl http://test-edge.com/ -H "X-Forwarded-For: 104.28.243.40, 105.56.18.52"
# 预期输出:105.56.18.52
# 说明:多值时默认取列表最后一个 IP;启用递归搜索后将跳过可信 IP,取第一个非可信地址

不可信来源验证:

# 从不在可信列表中的主机发出请求
curl http://test-edge.com/ -H "X-Forwarded-For: 104.28.243.40"
# 预期输出:该主机自身的出口 IP(如 203.0.113.5)
# 说明:来源不可信,XFF 头被忽略,client-addr 回退为 TCP 连接来源 IP

6.2 curl 携带 X-Real-IP

前提:Global Config 已选择 X-Real-IP 来源,请求来自可信代理。

curl http://test-edge.com/ -H "X-Real-IP: 104.28.243.40"
# 预期输出:104.28.243.40
# 说明:网关从 X-Real-IP 头取值,更新 client-addr

6.3 curl 携带自定义 HTTP 头

前提:Global Config 已选择 Custom Header 来源,并填写自定义头名称(示例:X-My-Real-IP)。请求来自可信代理。

curl http://test-edge.com/ -H "X-My-Real-IP: 104.28.243.40"
# 预期输出:104.28.243.40
# 说明:网关从配置中指定的自定义头字段取值,信任规则与其他方案一致

6.4 curl 携带 proxy-protocol

前提:目标端口已在网关分区启用 proxy protocol(见 3.1 节),Global Config 已选择 Proxy Protocol 来源。

标准 curl 默认不发送 proxy protocol 头,可通过以下方式验证:

方式一:使用 curl 内置参数(v7.60.0+)

curl http://test-edge.com/ --haproxy-protocol
# 预期输出:本机出口 IP
# 说明:curl 自动在 TCP 流头部注入 PROXY 协议头,网关从中提取客户端 IP

方式二:使用 nc 手动构造,模拟指定真实 IP

printf "PROXY TCP4 104.28.243.40 52.53.251.226 12345 80\r\nGET / HTTP/1.0\r\nHost: test-edge.com\r\n\r\n" \
  | nc test-edge.com 80
# 预期输出:104.28.243.40
# 说明:手动构造合规的 PROXY 协议头,网关解析后将 proxy_protocol_addr 和 client- addr 均设为 104.28.243.40

7. 延展阅读

OpenResty Edge 支持在回源请求中通过自定义请求头(如 X-Real-IP、X-Forwarded-For)将真实客户端 IP 透传给后端服务器,源站无需任何网络层改造即可直接读取。

推荐参考以下两篇官方文档深入了解:

  1. 如何通过特殊的请求头传递真实的客户端 IP 地址到后端服务器 介绍如何在页面规则中配置回源请求头,将客户端 IP 注入到指定 Header 并透传至后端服务。

  2. 在 OpenResty Edge 中精准还原真实的客户端 IP 地址 完整讲解从"真实来源 IP 信任地址"配置到客户端地址验证的全流程,与本文内容互为补充。

关于 OpenResty Edge

OpenResty Edge 是一款专为微服务和分布式流量架构设计的全能型网关软件,由我们自主研发。它集流量管理、私有 CDN 构建、API 网关、安全防护等功能于一体,帮助您轻松构建、管理和保护现代应用程序。OpenResty Edge 拥有业界领先的性能和可扩展性,能够满足高并发、高负载场景下的苛刻需求。它支持调度 K8s 等容器应用流量,并可管理海量域名,轻松满足大型网站和复杂应用的需求。

关于作者

章亦春是开源 OpenResty® 项目创始人兼 OpenResty Inc. 公司 CEO 和创始人。

章亦春(Github ID: agentzh),生于中国江苏,现定居美国湾区。他是中国早期开源技术和文化的倡导者和领军人物,曾供职于多家国际知名的高科技企业,如 Cloudflare、雅虎、阿里巴巴, 是 “边缘计算“、”动态追踪 “和 “机器编程 “的先驱,拥有超过 22 年的编程及 16 年的开源经验。作为拥有超过 4000 万全球域名用户的开源项目的领导者。他基于其 OpenResty® 开源项目打造的高科技企业 OpenResty Inc. 位于美国硅谷中心。其主打的两个产品 OpenResty XRay(利用动态追踪技术的非侵入式的故障剖析和排除工具)和 OpenResty Edge(最适合微服务和分布式流量的全能型网关软件),广受全球众多上市及大型企业青睐。在 OpenResty 以外,章亦春为多个开源项目贡献了累计超过百万行代码,其中包括,Linux 内核、Nginx、LuaJITGDBSystemTapLLVM、Perl 等,并编写过 60 多个开源软件库。

关注我们

如果您喜欢本文,欢迎关注我们 OpenResty Inc. 公司的博客网站 。也欢迎扫码关注我们的微信公众号:

我们的微信公众号

翻译

我们提供了英文版原文和中译版(本文)。我们也欢迎读者提供其他语言的翻译版本,只要是全文翻译不带省略,我们都将会考虑采用,非常感谢!