OpenResty Edge 客户端真实 IP 全链路传递指南
在引入前置代理、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 protocol | TCP/UDP/TLS 链接头部数据 | ✅ | ✅ | ✅ |
| X-Forwarded-For | HTTP 头 | ❌ | ✅ | ✅ |
| X-Real-IP | HTTP 头 | ❌ | ✅ | ❌ |
| 自定义头 | 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-IP、True-Client-IP 等);无需修改前置代理的头部字段名。
限制条件:前置代理须配合写入指定头字段;仅适用于 HTTP/HTTPS。
3. OpenResty Edge 配置
3.1 网关分区端口启用 proxy-protocol 支持
使用 proxy protocol 方案时,必须先在端口级别开启接收支持,其他方案可跳过此步骤。
- 进入 Admin Web 控制台,导航至 网关集群 > 网关分区
- 选择目标分区,点击右侧 编辑 按钮
- 在端口列表中找到目标端口,点击右侧 编辑 按钮
- 勾选 启用 proxy protocol,保存配置
- 在应用配置中选择想要下发的分区
预留端口,请勿将以下端口用于业务配置:80、443(默认代理,公网);11212(共享 SSL Session,内网);8090(集群资源共享,内网);8091(节点状态查询,仅本地)。
3.2 全局配置启用相关协议
- 进入 Global Config 页面
- 在 客户端 IP 来源 中选择实际使用的方案:
| 选项 | 说明 |
|---|---|
| X-Forwarded-For | 从 XFF HTTP 头获取真实客户端 IP 和端口 |
| Proxy Protocol | 从 Proxy Protocol 协议头获取(须完成 3.1 节端口配置) |
| X-Real-IP | 从 X-Real-IP HTTP 头获取真实客户端 IP 和端口 |
| Custom Header | 从自定义 HTTP 头字段获取,需同时填写头字段名称 |
- (可选)开启 递归搜索 IP:当头字段存在多个 IP 时,从后向前查找第一个非可信地址作为真实来源 IP,适合多级代理场景。
- 保存后点击 发布,配置将推送至所有网关服务器,无需重载或重启。
3.3 配置可信来源 IP
Trusted hosts to set real IP 定义了哪些上游地址发来的 Real IP 头是可信的。只有当 TCP 连接的对端 IP 在此列表中时,OpenResty Edge 才会采信请求头中的真实 IP 并更新 client-addr;来自不可信地址的请求,其 Real IP 头将被忽略。
- 在 Global Config 页面找到 Trusted hosts to set real IP
- 输入前置代理服务器的 IP 地址(示例:
52.53.251.226),可添加多条 - 保存并发布
行为对比:
| 请求来源 | 携带头 | client-addr 结果 |
|---|---|---|
52.53.251.226(可信) | X-Forwarded-For: 104.28.243.40 | 104.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-addr 是 OpenResty Edge 中最终生效的客户端地址,贯穿所有与客户端 IP 相关的处理逻辑。
取值逻辑:
- 初始值为 TCP 连接的对端 IP(即前置代理地址)
- 若该 IP 在可信来源列表中,且请求携带有效的 Real IP 头(或 proxy protocol 头),则
client-addr被更新为解析出的真实 IP - 若对端 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_addr | Proxy Protocol 头原始值 | 否 | 审计、调试 |
client-addr | 综合信任规则后的生效值 | 是 | 业务逻辑、限速、日志 |
当来源 IP 通过可信验证时,两者值相同。当来源 IP 不可信时,proxy_protocol_addr 仍保留协议头中的解析值,而 client-addr 不会被更新,仍为 TCP 连接来源 IP。在需要同时记录原始协议头值和最终生效 IP 的审计场景中,可分别引用两个变量。
5. 页面规则
本节配置一条 EdgeLang 页面规则,使请求响应中直接返回网关识别到的 client-addr,作为验证各 IP 传递方案是否生效的基础。
操作步骤:
- 进入目标应用(示例:
test-edge.com),选择 Page Rules - 点击 Edit(或新建规则)
- 使用 EdgeLang 编写规则:
# 条件:true(对所有请求生效)
# 动作:在响应体中输出当前 client-addr 的值
true =>
say(client-addr),
done;
- 保存并发布,配置推送至所有网关服务器,无需重启
发布后,对该域名的任何请求都将在响应中返回 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 透传给后端服务器,源站无需任何网络层改造即可直接读取。
推荐参考以下两篇官方文档深入了解:
如何通过特殊的请求头传递真实的客户端 IP 地址到后端服务器 介绍如何在页面规则中配置回源请求头,将客户端 IP 注入到指定 Header 并透传至后端服务。
在 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、LuaJIT、GDB、SystemTap、LLVM、Perl 等,并编写过 60 多个开源软件库。
关注我们
如果您喜欢本文,欢迎关注我们 OpenResty Inc. 公司的博客网站 。也欢迎扫码关注我们的微信公众号:
翻译
我们提供了英文版原文和中译版(本文)。我们也欢迎读者提供其他语言的翻译版本,只要是全文翻译不带省略,我们都将会考虑采用,非常感谢!

















