Nginx 无法在 TLS 握手阶段按地理位置动态切换 SSL 证书,因地理变量尚未可用;可行方案包括:多域名+地理 DNS 解析、边缘网关前置分流、OpenResty+Lua 动态加载、统一 SAN/通配符证书。

Nginx 本身无法在 TLS 握手阶段按访问者地理位置动态切换 SSL 证书,因为此时 HTTP 请求头(如 X-Forwarded-For 或地理信息变量)尚未解析,$geoip_country_code 等变量不可用。SNI 只能传递域名,不携带地域信息。所以“单靠 Nginx 配置实现地理感知证书分发”在技术上不可行。但可通过架构协同达成等效目标,关键是把地理判断提前到 TLS 层之前或之外。
多域名 + 地理 DNS 智能解析
这是最成熟、低运维、高兼容的方案。核心是让用户通过不同子域名访问对应地域的服务节点,每个子域名绑定专属证书。
- 为各区域规划独立子域名:如
cn.example.com(中国)、us.example.com(美国)、eu.example.com(欧洲) - 在 DNS 服务商(如 Cloudflare、阿里云云解析、DNSPod)启用「智能解析」或 GSLB 功能,按用户 IP 地理位置返回对应 A/AAAA 记录
- 每个后端 Nginx 实例只部署一个子域名的证书,
server_name明确限定,ssl_certificate和ssl_certificate_key指向本地文件 - 业务逻辑可统一反向代理至同一套后端服务,仅证书与入口节点分离
边缘网关前置地理分流
将 TLS 终止和地理路由交给更外层的云服务,Nginx 退为纯 HTTP 代理,彻底规避其证书选择限制。
- 使用 Cloudflare、AWS CloudFront 或阿里云全站加速作为入口,它们原生支持
$cf_ipcountry、$http_x_amz_cf_pop等地理变量 - 在边缘配置规则:例如欧盟用户请求转发至
nginx-eu.internal,日本用户转发至nginx-jp.internal - 各 Nginx 实例监听内部端口(如 8080),不再暴露 443,也不再管理公网 SSL 证书;证书由边缘自动托管与轮换
- 若需内网加密(如 mTLS),可在 upstream 中配置上游证书,但无需按地域切 HTTPS 证书
OpenResty + Lua 动态证书加载(进阶)
适用于有较强定制能力、且必须在 Nginx 层做决策的场景。需替换为 OpenResty,并依赖 OpenSSL 1.1.1+ 与 Lua-openssl 支持。
- 利用
ssl_certificate_by_lua_block在 SNI 阶段后、证书发送前执行 Lua 脚本 - 脚本中通过
ngx.var.ssl_server_name获取域名,再结合预加载的 GeoIP 数据库(如 mmdb)查出客户端国家码 - 根据结果动态调用
ssl.clear_certs()和ssl.set_der_cert()/ssl.set_der_priv_key()加载对应证书内容(需提前读入内存或缓存) - 注意:该方式对性能敏感,证书不能频繁重载;需严格校验证书链有效性,且不兼容所有客户端(尤其旧 TLS 栈)
统一 SAN 或通配符证书覆盖全地域
最简方案——放弃“差异化”,用一张证书覆盖所有目标域名,消除分发需求。
- 申请含全部子域名的 SAN 证书(如
cn.example.com,us.example.com,eu.example.com) - 或使用通配符证书(如
*.example.com),注意它只覆盖一级子域,不包含根域example.com,需额外添加 - 所有 Nginx server 块共用同一组
ssl_certificate和ssl_certificate_key - 适合品牌一致性要求高、合规策略统一、且无地域专属合规证书(如国密 SM2)需求的场景


















