c.ip() 默认返回代理内网ip,必须配置proxyheader(如x-forwarded-for)并确保nginx等透传对应头,再结合sync.map黑白名单、ipv6标准化比对及私有地址过滤,才能正确拦截。

如何用 Fiber 的 Next 和 c.IP() 实现基础 IP 拦截
直接在中间件里取客户端真实 IP,调用 c.IP() 获取后查黑白名单即可。但注意:默认 c.IP() 可能返回反向代理(如 Nginx)的内网地址,不是用户真实 IP。
必须提前配置 Fiber 的 ProxyHeader 选项,否则中间件永远拦错人:
app := fiber.New(fiber.Config{
ProxyHeader: fiber.HeaderXForwardedFor,
})
- 如果用 Cloudflare,要设为
fiber.HeaderCFConnectingIP - 如果 Nginx 透传了
X-Real-IP,则设为fiber.HeaderXRealIP - 多个头同时存在时,Fiber 默认只认第一个非空值,不自动 fallback
黑白名单数据结构选 map[string]bool 还是 sync.Map
如果名单是启动时加载、运行中不变更,用普通 map[string]bool 最快;若需热更新(比如从数据库/Redis 动态拉取),必须用 sync.Map 或加读写锁。
常见错误:直接在中间件里每次读文件或查 DB——QPS 上去就拖垮整个服务。
- 建议启动时初始化
sync.Map,另起 goroutine 定期刷新 - 避免在中间件里做
http.Get或db.QueryRow调用 - 白名单优先级高于黑名单:IP 同时在两者中,应放行
拦截逻辑里怎么处理 IPv6 和私有地址
Fiber 的 c.IP() 返回的是字符串,IPv6 地址带冒号(如 ::1),而黑名单配置项可能是压缩格式(::1)或全展开格式(0000:0000:0000:0000:0000:0000:0000:0001),直接 == 会失败。
更关键的是:别忘了过滤本地回环和私有网段(如 127.0.0.1、192.168.0.0/16)。这些地址如果出现在黑名单里,可能误杀本机健康检查或内网调用。
- 用
net.ParseIP()+net.IP.To16()统一转为 16 字节再比较 - 私有地址判断用
ip.IsPrivate()(Go 1.19+)或手动匹配 CIDR - 开发环境常把
localhost加进白名单,但c.IP()不解析域名,得提前映射
返回响应时别用 c.Status(403).SendString() 硬编码
硬编码响应体不利于统一错误格式,也难做审计日志。应该复用项目已有的错误构造逻辑,比如封装成 ErrorResponse 结构体。
另一个坑:拦截后忘记调用 c.Next() 或错误地调用了——该拦截时不调 Next,该放行时才调。
- 正确模式:
if isBlocked { c.Status(403).JSON(...); return },后面直接写业务路由逻辑 - 别写
if !isBlocked { c.Next() },这会导致没被拦的请求走两遍Next - 日志里至少记录 IP、时间、是否命中黑名单/白名单,别只记“被拦截”
ProxyHeader 配置要和最后一跳代理的行为对齐。差一个 header 名字,整个中间件就形同虚设。











