模拟不同地区IP进行测试的几种方法

问题背景

前段时间在做智能路由的功能,需要根据用户的 IP 地址判断地理位置,然后把请求路由到最近的服务节点。本地开发的时候,怎么模拟不同地区的 IP 成了个问题。

总不能真的去各个国家部署测试机器吧?太折腾了。后来发现其实有几种简单的方法可以模拟。

方法一:修改 HTTP 请求头(最常用)

大部分 Web 应用在反向代理后面(Nginx、Cloudflare 这些),服务端获取客户端 IP 主要靠 HTTP 请求头。直接修改这些头就能”伪造”IP。

原理

当请求经过反向代理时,代理会在请求头里加上客户端的真实 IP。常见的头有:

  • X-Forwarded-For:记录请求经过的所有代理 IP 链,格式是 客户端IP, 代理1IP, 代理2IP
  • X-Real-IP:客户端的真实 IP(通常由 Nginx 设置)
  • CF-Connecting-IP:Cloudflare 用的头

服务端拿 IP 的时候,优先级一般是这样的:

用 curl 测试

1
2
3
4
5
6
7
8
9
10
11
12
13
# 模拟美国IP(Google DNS)
curl -H "Host: your-domain.com" \
-H "X-Forwarded-For: 8.8.8.8" \
http://your-server.com/api/check-location

# 模拟中国IP(腾讯DNS)
curl -H "Host: your-domain.com" \
-H "X-Forwarded-For: 119.29.29.29" \
http://your-server.com/api/check-location

# 模拟多个代理的情况
curl -H "X-Forwarded-For: 8.8.8.8, 203.0.113.1, 198.51.100.1" \
http://your-server.com/api/check-location

Go 服务端获取 IP 的代码

那天写代码的时候,踩了个坑:X-Forwarded-For 可能包含多个 IP,要取第一个才是客户端 IP。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import (
"net"
"strings"
)

func GetClientIP(r *http.Request) string {
// 优先检查 X-Forwarded-For
if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
// 可能有多个IP,取第一个(客户端IP)
if idx := strings.Index(xff, ","); idx != -1 {
return strings.TrimSpace(xff[:idx])
}
return strings.TrimSpace(xff)
}

// 其次检查 X-Real-IP
if xri := r.Header.Get("X-Real-IP"); xri != "" {
return strings.TrimSpace(xri)
}

// 最后才用 RemoteAddr(直连的情况)
if ip, _, err := net.SplitHostPort(r.RemoteAddr); err == nil {
return ip
}

return r.RemoteAddr
}

这个函数有个前提:必须信任反向代理传过来的头。不然任何人都能伪造 IP,后面会说安全问题。

方法二:使用代理服务器

如果服务端直接从 TCP 连接拿 IP(不看 HTTP 头),那就得上真正的代理。

用 VPN 或 SOCKS 代理

1
2
3
4
5
6
7
# 通过 SOCKS5 代理发送请求
curl --socks5 127.0.0.1:1080 \
http://your-server.com/api/check-location

# 或者用所有请求都走代理
export ALL_PROXY=socks5://127.0.0.1:1080
curl http://your-server.com/api/check-location

这个方法麻烦点,但更真实。服务端拿到的 RemoteAddr 就是代理服务器的 IP。

常用的测试 IP

搞了一份常用的测试 IP,都是公共服务 DNS,稳定可用:

地区 IP地址 说明
美国加州 8.8.8.8 Google DNS
美国加州 1.1.1.1 Cloudflare DNS
中国广州 119.29.29.29 腾讯 DNSPod
中国香港 210.0.0.58 香港电讯 DNS
日本东京 202.12.27.33 APNIC DNS
英国伦敦 77.88.8.1 Yandex DNS

[配图: IP地理位置查询工具的截图,显示这些IP的地理位置]

安全问题:IP 伪造的风险

前面提到,直接修改 HTTP 头伪造 IP 有个严重的安全问题:任何人都能改

如果不验证 HTTP 头的来源,攻击者可以:

  • 伪造 IP 绕过地理位置限制
  • 伪造 IP 进行刷票、刷接口
  • 伪造 IP 躲避封禁

生产环境的正确做法

  1. 在反向代理层面处理:Nginx 或 Cloudflare 负责解析真实 IP,设置 X-Real-IP
  2. 服务端只信任代理的头:比如只信任内网 Nginx 传过来的 X-Real-IP
  3. 限制可信代理范围:用防火墙规则,只允许特定的反向代理访问
1
2
3
4
5
# Nginx 配置示例
set_real_ip_from 10.0.0.0/8; # 内网代理段
set_real_ip_from 172.16.0.0/12;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

这样服务端拿到的 IP 才是可信的。

实际应用场景

除了测试智能路由,这个技巧还有很多用处:

  • 测试 A/B 实验:根据地区投放不同功能
  • 验证 CDN 缓存:不同地区的节点缓存是否生效
  • 模拟攻击来源:测试防刷、限流策略
  • 调试 IP 定位:验证 IP 地理位置库的准确性

那天排查一个 bug,用户用国外vpn 反馈访问慢,就是用这个方法复现的。伪造了几个不同国家的 IP,发现确实路由到了错误的服务节点。

总结

模拟不同地区 IP 进行测试,核心就是理解服务端是怎么获取客户端 IP 的:

  • 有反向代理的情况:修改 HTTP 头(X-Forwarded-ForX-Real-IP)最方便
  • 直连的情况:得用真正的代理服务器
  • 生产环境:必须验证 HTTP 头的来源,防止 IP 伪造

本地测试时用方法一就够,简单快捷。但上线前一定要考虑安全问题,不然容易被钻空子。


延伸阅读

  • Nginx ngx_http_realip_module 文档
  • Cloudflare CF-Connecting-IP 头说明