map 指令比 location 块更合适,因其在请求早期运行、支持子域名匹配、可复用变量且避免配置臃肿;通过 $host 动态映射 CSP 策略,适配多区域站点差异化安全需求。

用 map 指令动态设置 CSP,核心是把策略值变成变量,再根据请求特征(比如域名、路径、地域标识)实时计算出对应策略,避免硬编码多个 location 块或重复配置。对多区域静态站点(如 cn.example.com、us.example.com、jp.example.com)尤其适用——不同地区可能使用不同 CDN、字体服务或分析脚本,策略不能一刀切。
为什么 map 比 location 块更合适
map 是 Nginx 的变量映射模块,运行在请求处理早期,支持正则、前缀匹配和嵌套逻辑;而 location 块只作用于 URI 路径,无法直接区分子域名策略,且大量 location 嵌套会让配置臃肿难维护。更重要的是:map 定义的变量可在任意作用域(server、location)中复用,天然适配多区域场景。
配置步骤:从域名识别到策略生成
以三个区域站点为例,假设需为 CN 站点放行国内 CDN 和统计 JS,US 站点放行 Google Fonts 和 GA4,JP 站点限制更严仅允同源:
- 在
http块顶部启用map(宝塔面板支持,但需确认 Nginx 版本 ≥1.11):
map $host $csp_default_src {
~\.cn\.example\.com "'self' https://cdn-cn.example.com";
~\.us\.example\.com "'self' https://fonts.googleapis.com https://www.googletagmanager.com";
~\.jp\.example\.com "'self'";
default "'self'";
}
- 再定义 script-src 变量,与 default-src 解耦(因脚本策略常更严格):
map $host $csp_script_src {
~\.cn\.example\.com "'self' 'unsafe-inline' https://s95.cnzz.com";
~\.us\.example\.com "'self' 'unsafe-inline' https://www.googletagmanager.com";
~\.jp\.example\.com "'self'";
default "'self'";
}
- 在
server块中统一注入 CSP 头(注意必须加always):
server {
server_name ~^(?
add_header Content-Security-Policy "default-src $csp_default_src; script-src $csp_script_src; style-src 'self' 'unsafe-inline'; img-src 'self' data:;" always;
# 其他静态文件配置…
}
关键细节和避坑点
-
正则匹配要加
~\.:防止cn.example.com错误匹配到example.com(无前缀点),$host是原始 Host 头,大小写敏感,建议全小写写法 -
所有变量值必须用双引号包裹:Nginx 解析时会拼接字符串,若
$csp_script_src含空格或分号,不加引号会导致语法错误 - 避免与后端冲突:确保应用代码(如 PHP/Node.js)不输出 CSP 头,否则浏览器可能收到两个 CSP,策略以第一个为准或被忽略
-
测试 report-only 模式:上线前先用
Content-Security-Policy-Report-Only替换 header 名,并配report-to或report-uri收集违规日志,验证策略是否误杀合法资源
扩展:结合请求路径做二级动态
若同一区域下还有管理后台(/admin/)需更宽松策略,可叠加 map:
map $request_uri $csp_admin_flag {
~^/admin/ "1";
default "0";
}
map $csp_admin_flag $csp_script_admin {
"1" "'self' 'unsafe-inline' 'unsafe-eval'";
"0" "$csp_script_src";
}
然后在 add_header 中引用 $csp_script_admin 即可,无需新增 location /admin/ 块。










