不推荐用 rewrite 做地域子域名分发,应基于 geoip2 识别真实 ip 地域,再通过 map 映射后端 upstream,最后用 proxy_pass 透明转发;仅 seo 等极少数场景才用 rewrite 跳转。

直接用 rewrite 做地域子域名分发不现实,也不推荐。Nginx 的 rewrite 是路径重写工具,不是路由决策引擎;子域名分流 + 地域识别需要的是“识别 → 映射 → 转发”三层配合,核心靠 geoip2 + map + proxy_pass,而不是 rewrite。
先识别真实地域,不是靠 Host 头
用户请求带的是 cn.example.com 或 us.example.com,但这个子域名可能是前端硬编码、DNS 预设或客户端伪造的,不可信。真正可靠的地域来源是客户端 IP:
- 必须启用
geoip2模块加载 MaxMind 数据库(如GeoLite2-Country.mmdb) - 在
http块中定义地理字段提取:
geoip2 /path/to/GeoLite2-Country.mmdb {
$geoip2_data_country_code source=$realip_remote_addr country iso_code;
} - 关键前提:用
set_real_ip_from+real_ip_header X-Forwarded-For还原真实客户端 IP,否则查的是 CDN 或 LB 的 IP
再把地域映射成子域名对应后端
不写 if,不拼接字符串,用 map 预定义映射关系,性能高、可维护性强:
- 例如让中国用户走
cn.example.com后端,美国用户走us.example.com后端:
map $geoip2_data_country_code $upstream_backend {
CN cn_backend;
US us_backend;
default default_backend;
} - 每个
upstream独立定义,指向真实服务地址(可跨机房,但建议同中心):
upstream cn_backend { server 10.1.10.5:8080; }
upstream us_backend { server 10.2.20.8:8080; }
最后在 server 块里统一入口,按变量转发
所有子域名(*.example.com)都解析到同一组 Nginx,由它根据地域决定实际打给哪个 upstream:
- server 块只监听泛域名:
server {
listen 80;
server_name ~^(?.+)\.example\.com$;
location / {
proxy_pass http://$upstream_backend;
proxy_set_header Host $host;
}
} - 注意:
$upstream_backend是 map 计算出的变量名,不是字符串拼接;Nginx 会自动解析并路由到对应 upstream - 无需 rewrite 子域名,也不需要返回 301;这是纯服务端内部转发,对客户端完全透明
补充:如果真要改子域名(比如跳转),才用 rewrite
极少数场景需显式跳转(如 SEO 强制规范访问入口),这时才用 rewrite + geoip2 结合:
- 例如访问
example.com时,自动 301 跳到对应地域子域名:
if ($geoip2_data_country_code = "CN") {
return 301 https://cn.example.com$request_uri;
}
if ($geoip2_data_country_code = "US") {
return 301 https://us.example.com$request_uri;
} - 但这种跳转会暴露子域名、增加 RTT、影响首屏,生产环境应优先走透明代理,而非重定向











