应使用 Nginx 的 map 指令结合 GeoIP2 模块($geoip2_data_country_code)按国家代码动态生成 CSP 策略片段,再通过 add_header 组装生效;需正确配置 real_ip_header 和 set_real_ip_from 以确保地域识别准确,并经 curl 模拟和 Report-Only 模式验证后灰度上线。

直接在 Nginx 中用 add_header 写死 CSP 策略,无法适配不同地理区域的合规要求(如欧盟 GDPR 对 tracker 的限制、中国对 CDN 域名白名单的强制要求、东南亚部分国家对第三方支付脚本的特殊放行)。靠手动维护多套配置或后端动态注入,既难统一又易出错。真正可行的方式是:用 map 指令将地理位置映射为策略片段,再由 add_header 组装生效。
用 $geoip2_country_code 或 $http_x_forwarded_for + GeoIP2 模块识别区域
Nginx 本身不自带 IP 地理定位,需启用 ngx_http_geoip2_module 并加载 MaxMind GeoLite2 数据库。配置后可获得精确到国家的变量:
-
$geoip2_data_country_code(如"DE"、"CN"、"SG")是最稳定、推荐使用的源变量 - 避免用
$remote_addr直接查库——若前端经过 CDN 或 LB,该值可能是代理 IP;应确保real_ip_header X-Forwarded-For和set_real_ip_from已正确配置,使$geoip2_data_country_code反映真实用户属地 - 若暂未部署 GeoIP2,可用
map配合可信 CDN 提供的地理头(如 Cloudflare 的$http_cf_ipcountry)作为临时替代
按国家代码定义差异化 CSP 策略片段
将策略拆解为可组合的字符串片段,用 map 分别映射。这样既清晰又便于审计:
- 定义脚本源策略:
map $geoip2_data_country_code $csp_script_src {<br> default "'self' https://cdn.example.com";<br> "EU" "'self' https://cdn.example.com 'unsafe-hashes'";<br> "CN" "'self' https://cdn.example.com https://gcore.jsdelivr.net";<br> "SG" "'self' https://cdn.example.com https://static.paynow.sg";<br>} - 定义连接源(connect-src),用于 API 调用控制:
map $geoip2_data_country_code $csp_connect_src {<br> default "'self'";<br> "CN" "'self' https://api-cn.example.com";<br> "US" "'self' https://api-us.example.com";<br>} - 对敏感区域(如 EU)额外收紧:
map $geoip2_data_country_code $csp_sandbox {<br> "EU" "sandbox allow-scripts allow-same-origin";<br> default ""; <br>}
在 server 或 location 中组装并注入完整 CSP 头
使用 add_header 将多个 map 变量拼接成合法 CSP 字符串。注意以下关键点:
- 必须在
http块中定义所有map,否则变量不可用 - 拼接时保留单引号包裹关键词(如
'self'),空格分隔各指令,末尾不加多余空格 - 示例配置:
server {<br> server_name app.example.com;<br> add_header Content-Security-Policy "default-src 'none'; script-src $csp_script_src; connect-src $csp_connect_src; img-src 'self' data:; style-src 'self' 'unsafe-inline'; $csp_sandbox;";<br>} - 若某区域需完全禁用内联脚本,可将
$csp_script_src映射为"'self'",并移除'unsafe-inline'—— 这比全局关闭更精准
验证与灰度上线要点
地理感知 CSP 极易因变量为空或拼接错误导致策略失效,上线前必须验证:
- 用
curl -I https://app.example.com -H "X-Forwarded-For: 8.8.8.8"模拟不同地区 IP,并检查响应头中Content-Security-Policy是否含预期域名和指令 - 确认浏览器开发者工具的 Security 标签页中显示的策略与响应头一致(注意大小写敏感,Nginx 输出的 header 名必须全小写)
- 先对小流量路径(如
/health)启用,再逐步扩大到/;生产环境建议搭配Content-Security-Policy-Report-Only模式收集违规报告 3–5 天后再切正式策略 - 若使用 Ingress-Nginx,需通过
nginx.ingress.kubernetes.io/configuration-snippet注入map块,并确保 controller 版本 ≥ 1.9 支持 GeoIP2 变量










