Nginx 通过 map 指令在 http 块中预定义变量实现动态 CSP 策略,再用 add_header $var always 引用;支持基于域名、请求头、参数或 GeoIP 地理信息映射不同策略,需注意语法规范与配置位置。

Nginx 本身不支持在 add_header 指令中直接使用变量(如 $arg_region 或 $http_x_forwarded_for)来动态拼接 CSP 值,因为 add_header 是静态指令,解析发生在配置加载阶段,而非请求处理阶段。但你可以借助 map 指令预先将条件映射为字符串变量,再在 add_header 中引用该变量——这是生产环境实现多区域 CSP 策略最稳定、最推荐的方式。
用 map 定义区域策略变量
map 必须定义在 http 块顶层(不能在 server 或 location 内),它根据请求特征(如域名、请求头、参数或 IP 地理信息)映射出预设的 CSP 字符串值。例如:
(放在 nginx.conf 的 http { ... } 块中)
map $host $csp_policy {
default "default-src 'self'; script-src 'self' https://cdn.example.com; img-src 'self' data:;";
"cn.example.com" "default-src 'self'; script-src 'self' https://cdn.example.com 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src *;";
"us.example.com" "default-src 'self'; script-src 'self' https://www.php.cn/link/2800a901597876b19a05fa2ba197a966 https://analytics.google.com; connect-src 'self' https://www.php.cn/link/f0b314f185b80cf35d986e298db53fe3;";
"~^([a-z]{2})\.example\.com$" "$1-csp";
}
<h1>可选:二级映射,让正则匹配更灵活</h1><p>map $1 $region_csp {
"cn" "default-src 'self'; script-src 'self' <a href="https://www.php.cn/link/e73dbb390cb1dd2a3e9d71c3ac806044">https://www.php.cn/link/e73dbb390cb1dd2a3e9d71c3ac806044</a>; font-src 'self' <a href="https://www.php.cn/link/9ec3cff2fc5008ded145994ef2b906d7">https://www.php.cn/link/9ec3cff2fc5008ded145994ef2b906d7</a>;";
"us" "default-src 'self'; script-src 'self' <a href="https://www.php.cn/link/2800a901597876b19a05fa2ba197a966">https://www.php.cn/link/2800a901597876b19a05fa2ba197a966</a>; connect-src 'self' <a href="https://www.php.cn/link/f0b314f185b80cf35d986e298db53fe3">https://www.php.cn/link/f0b314f185b80cf35d986e298db53fe3</a>;";
"eu" "default-src 'self'; script-src 'self' <a href="https://www.php.cn/link/5b9320355914e4428c01f4427cce9bd1">https://www.php.cn/link/5b9320355914e4428c01f4427cce9bd1</a>; frame-src 'self' <a href="https://www.php.cn/link/2ad85be364c615d10de8a25d2af95c34">https://www.php.cn/link/2ad85be364c615d10de8a25d2af95c34</a>;";
default "default-src 'self';";
}</p>
注意:map 不支持嵌套或复杂逻辑,所有策略必须是完整、合法的 CSP 字符串,且需严格遵循语法规范(英文引号、分号+空格分隔、'self' 不可漏引号等)。
在 server 块中引用并输出 CSP 头
在对应站点的 server 块中,使用 add_header ... always; 引用 map 生成的变量。确保加 always,否则 4xx/5xx 响应不会携带该头:
server {
listen 443 ssl;
server_name example.com *.example.com;
<pre class="brush:php;toolbar:false;"># 其他配置...
add_header Content-Security-Policy $csp_policy always;
# 或使用二级映射
# add_header Content-Security-Policy $region_csp always;
}
若需基于请求头(如 X-Region)或 URL 参数(如 ?region=cn)区分策略,可将 map 的 key 改为 $http_x_region 或 $arg_region,但要注意客户端可控参数存在被篡改风险,仅建议用于灰度或内部测试,生产环境优先用域名或可信反向代理头(如 X-Forwarded-For 配合 GeoIP 模块)。
配合 GeoIP 或自定义头实现地理感知策略
如果需按真实用户地理位置下发不同 CSP(如欧盟用户强制启用 report-to,中国用户允许特定 CDN),可结合 ngx_http_geoip2_module:
- 先用 geoip2 模块提取
$geoip2_data_country_code - 再用
map $geoip2_data_country_code $geo_csp映射策略 - 最后在
add_header中引用$geo_csp
示例片段:
map $geoip2_data_country_code $geo_csp {
"CN" "default-src 'self'; script-src 'self' https://cdn.cn; report-uri /csp-report-cn;";
"US" "default-src 'self'; script-src 'self' https://cdn.us; report-to csp-us-endpoint;";
"DE" "default-src 'self'; script-src 'self'; report-to csp-eu-endpoint; require-sri-for script style;";
default "default-src 'self';";
}
注意:GeoIP 数据库需定期更新,且首次请求时可能因延迟影响首屏性能,建议搭配缓存或异步加载策略。
验证与调试关键点
上线前务必验证三件事:
- 执行
nginx -t确保map语法无误,变量名不冲突 - 用
curl -I https://cn.example.com和curl -I https://us.example.com分别检查响应头中Content-Security-Policy是否正确输出 - 打开 Chrome DevTools → Network → 刷新页面 → 查看文档请求的 Response Headers,确认策略未被截断、无中文符号、无多余空格
若策略未生效,常见原因是:map 定义位置错误(不在 http 块)、变量名拼写不一致、缺少 always、或 Nginx 版本低于 1.7.0(不支持 map 在 header 中引用)。










