ctx.IP() 返回 Nginx 或 CDN 内网地址是因为默认读取 req.RemoteAddr,即直连对端地址;需配置 Nginx 透传 X-Forwarded-For 和 X-Real-IP,并自定义 getClientIP() 校验可信代理与私有 IP。

为什么 ctx.IP() 返回的是 Nginx 或 CDN 的内网地址
ctx.IP() 默认只读 req.RemoteAddr,而它返回的是与 Fiber 服务**直连的对端地址**——比如 Nginx 的 10.0.0.5:42193、CDN 边缘节点 IP,甚至本地开发时的 127.0.0.1。这不是 Fiber 的 bug,是 TCP 层事实:HTTP 请求经过代理后,原始 socket 源地址就被覆盖了。
必须依赖 HTTP header 显式透传真实 IP,且 Nginx(或任何前置代理)和 Fiber 解析逻辑要严格匹配,否则限流、风控、日志全会失效。
必须先配 Nginx:proxy_set_header 两行不能少
Nginx 不传,Fiber 就没得解析。只加 X-Forwarded-For 不够,X-Real-IP 是 fallback 关键:
-
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;—— 自动追加,不覆盖已有值 -
proxy_set_header X-Real-IP $remote_addr;—— 确保最可信的单个 IP 可用
漏掉任意一行,X-Forwarded-For 可能为空,X-Real-IP 可能未设置,Fiber 就只能退到 RemoteAddr。
自己写 getClientIP(),别信 ctx.IP() 默认行为
Fiber v3 的 ctx.IP() 默认不校验代理可信性,直接取 X-Forwarded-For 首项,有被伪造风险。你应该写一个带校验的函数:
关键逻辑:
- 先检查
req.RemoteAddr解析出的 IP 是否在你信任的代理网段(如10.0.0.0/8、172.16.0.0/12) - 只有可信时,才从
X-Forwarded-For中取最左的非私有 IP(跳过127.0.0.1、192.168.0.0/16等) - 全为私有 IP 时,fallback 到
X-Real-IP
示例片段(20 行内):
func getClientIP(c *fiber.Ctx) string {
ip, _, _ := net.SplitHostPort(c.Context().RemoteAddr().String())
if !isTrustedProxy(ip) {
return ip
}
if xff := c.Get("X-Forwarded-For"); xff != "" {
for _, ip = range strings.Split(xff, ",") {
ip = strings.TrimSpace(ip)
if !isPrivateIP(ip) {
return ip
}
}
}
return c.Get("X-Real-IP")
}
别忽略 Fiber.Config.TrustProxy 的陷阱
Fiber 提供了 TrustProxy 配置,但它的默认行为是信任所有代理(TrustProxy: true),等价于无条件取 X-Forwarded-For 首项——这和手写函数相比更不安全。
若要用它,必须显式配置可信网段:
-
TrustProxy: []string{"10.0.0.0/8", "172.16.0.0/12"}✅ -
TrustProxy: true❌(生产禁用)
注意:TrustProxy 配置只影响 ctx.IP() 和 ctx.IPs(),不影响你自定义的 getClientIP();它底层仍不校验私有 IP,所以 fallback 逻辑依然要自己补。
真正容易被忽略的点是:私有 IP 判断必须逐个做,且 net.ParseIP(ip).IsPrivate() 对 IPv6 地址也有效;很多实现只判 IPv4,导致 IPv6 真实用户 IP 被错误丢弃。


















