fiber.ipfilter易失效,因默认只读x-forwarded-for,若nginx未配置proxy_set_header透传真实ip或未设app.config.proxyheader与trustedproxies,会导致ip识别为127.0.0.1或伪造值。

为什么直接用 fiber.IPFilter 容易失效
很多人一上来就调 fiber.IPFilter,发现根本拦不住请求——不是配置错,而是它默认只检查 X-Forwarded-For,而你没配反向代理(比如 Nginx)透传真实 IP,或者客户端压根没带这个头。结果所有请求都被当成 127.0.0.1 或网关 IP 处理,规则全乱套。
真正起作用的前提是:你的服务能拿到客户端真实 IP。要么直连(开发环境),要么确保前置代理设置了 proxy_set_header X-Forwarded-For $remote_addr; 且 Fiber 启用了信任代理(app.Config.ProxyHeader 设为 X-Forwarded-For)。
- 不设
app.Config.ProxyHeader→ctx.IP()永远返回负载均衡器或 Nginx 的 IP - 设了但 Nginx 没透传
X-Forwarded-For→ctx.IP()返回空或错误值 - 信任了代理但没限制信任范围(如
app.Config.TrustedProxies)→ 可被伪造 IP 绕过
用 fiber.Limiter 做 IP 级限流的正确姿势
fiber.Limiter 是最常用也最稳妥的方式,它基于 IP 自动提取、支持内存/Redis 后端、可自定义键生成逻辑。关键不是“怎么加中间件”,而是“怎么让键真正代表唯一客户端”。
默认行为会用 ctx.IP(),所以必须先确认 IP 获取可靠;否则得手动覆盖 keyGenerator:
limiter := fiber.Limiter{
NewStore: store,
Max: 100,
Expiration: 30 * time.Second,
KeyGenerator: func(c *fiber.Ctx) string {
// 强制从 X-Real-IP 取,或 fallback 到 RemoteIP()
ip := c.Get("X-Real-IP")
if ip == "" {
ip = c.IP()
}
return ip
},
}
- 别依赖
ctx.IP()默认行为,显式控制来源更安全 - 如果用 Redis 存储,记得初始化
store := redis.New(...)并传入,否则跑在内存里重启就清零 -
Max和Expiration要匹配业务节奏:API 高频调用建议Max=50, Expiration=10s,防爆刷;管理后台可放宽到Max=5, Expiration=300s
硬性黑名单:用 fiber.IPFilter 拦特定 IP 段
fiber.IPFilter 适合封禁已知恶意 IP 或 CIDR 段,但它不计数、不滑动窗口,只是开关式过滤。常见误用是写死白名单却忘了放通本地调试地址(127.0.0.1),结果自己都访问不了。
正确写法要兼顾开发和生产:
ipFilter := fiber.IPFilter{
IPs: []string{"192.168.1.0/24", "203.0.113.42"},
Blocked: "IP not allowed",
AllowLocal: true, // 开发时保留 localhost 访问
}
-
AllowLocal默认 false,不打开的话127.0.0.1和::1全被拦 - CIDR 写法必须合法,
"192.168.1.*"这种字符串匹配会失效,得用"192.168.1.0/24" - 黑名单优先级高于限流中间件,放在路由链最前面才有效
组合策略:IP 限流 + 地理位置/UA 辅助识别
纯 IP 限制容易误伤(比如公司出口 NAT 共享一个公网 IP)。真要防攻击,得叠加其他维度,比如 UA 异常、请求路径高频重复、或结合 GeoIP 判定高危区域。
Fiber 本身不提供 GeoIP,但你可以用 maxminddb 库查库,在中间件里做增强判断:
func geoBlock(c *fiber.Ctx) error {
ip := c.IP()
record, _ := reader.City(net.ParseIP(ip))
if record.Country.IsoCode == "CN" && strings.Contains(c.Get("User-Agent"), "curl") {
return c.Status(fiber.StatusForbidden).SendString("Blocked")
}
return c.Next()
}
- GeoIP 查库有性能开销,务必加缓存(比如用
sync.Map缓存最近 1k 个 IP 结果) - UA 检查不能只看是否含
curl,得配合频率——单次 curl 请求不该被拦,10 秒内 50 次才可疑 - 这种组合逻辑别塞进
fiber.Limiter,单独写中间件,用c.Next()控制流程更清晰
IP 限制最麻烦的从来不是代码怎么写,而是你永远不知道前端有没有多层代理、CDN 是否改写了头、客户端会不会伪造 X-Forwarded-For。上线前一定用真实链路(比如 Cloudflare → Nginx → Fiber)抓包验证 ctx.IP() 输出是否符合预期。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











