Ruby on Rails在Heroku环境下获取的用户IP与设备实际IP不一致问题
Heroku环境下Rails获取真实用户IP的调整方案
根因定位
对照你贴的日志就能直接定位问题:
- Heroku平台输出的访问日志里,
fwd="136.xx.xx.xxx,172.xx.xx.xx"已经存了完整的请求转发链,第一个值就是你家庭测试设备的真实公网IP,第二个是Heroku内部代理的内网地址 - 应用日志里打印的
Started GET "/" for 198.xx.xx.xx,拿到的是Heroku边缘路由节点的出口IP,和真实用户位置完全无关
出现这个偏差的核心原因是:Rails默认的可信代理白名单只覆盖常规私有网段,没有把Heroku的代理链路纳入信任范围,框架不会主动从X-Forwarded-For(也就是日志里的fwd字段)里提取真实用户IP,只会把最后一跳直连到dyno的代理IP当作请求来源。
配置调整步骤
1. 修正生产环境可信代理规则
打开 config/environments/production.rb,调整可信代理的配置项,叠加Heroku专用的内部代理网段:
# config/environments/production.rb # 保留框架默认的可信私有网段,追加Heroku内部代理IP段 config.action_dispatch.trusted_proxies = [ ActionDispatch::RemoteIp::TRUSTED_PROXIES, IPAddr.new("172.16.0.0/12") ]
172.16.0.0/12是Heroku所有内部代理节点使用的固定网段,刚好覆盖你日志里看到的172开头的内网转发地址。配置完成后,Rails会自动把这个网段的代理判定为可信,从转发的IP链路上剥离所有可信代理的地址,把最靠前的真实用户IP作为请求来源IP返回。
2. 适配现有取数逻辑
可信代理规则生效后,不需要额外手动拆分转发头取IP:
- 直接用
request.remote_ip就能拿到经过安全校验的真实用户IP - 原有的
geocodergem、ipinfo_io组件会自动读取这个修正后的IP值,不会再拿到之前198段的边缘节点IP - 不要手动直接读取
request.headers['X-Forwarded-For']的第一个值,这种写法存在IP伪造的安全风险,必须依赖Rails自带的可信代理校验逻辑处理。
3. 效果验证
- 修改配置后重启dyno,让新规则生效
- 可以临时加个测试路由快速验证识别结果:
访问该路由返回的IP如果和你本地查询的公网IP一致,就说明配置已经生效,验证完记得删掉这个临时路由。# config/routes.rb get '/debug_ip', to: ->(env) { [200, {}, [env['action_dispatch.remote_ip'].to_s]] }
避坑提示
- 不要为了图省事把整个公网网段设为可信代理,会引入IP伪造的安全风险,只追加
172.16.0.0/12网段就完全匹配Heroku的转发链路 - 生产环境的配置有缓存,修改完如果不重启dyno,旧规则会一直生效
- 不要硬编码取
fwd字段的第一个值,Heroku的转发链路如果后续调整网段,硬编码逻辑会直接失效。
内容的提问来源于stack exchange,提问作者Splohr
相关产品推荐
相关产品推荐

