一、问题背景:为什么广告突然不显示了
近期在运营多个工具站和博客站(包括PHP的ZBlog、Go语言CMS、Flask应用)时,发现Google AdSense广告单元、GTM容器、GA4统计代码频繁出现加载失败的情况。排查浏览器控制台后,发现大量类似报错:
Refused to load the script 'https://pagead2.googlesyndication.com/...' because it violates the following Content Security Policy directive: "script-src 'self' ..."
根源在于Nginx配置中启用了强制执行的CSP(Content-Security-Policy)。虽然之前已经配置了大量Google相关域名白名单,但谷歌广告系统会动态引入新的脚本源和第三方追踪域名,白名单永远追不上变化。
二、Report-Only模式:不阻止只记录
对于以工具属性为主、不存在用户输入内容的站点,严格CSP的防护收益极低,反而严重影响广告变现和第三方服务接入。最佳实践是将CSP降级为Report-Only模式:
不拦截任何资源:浏览器不会阻止脚本、图片、Frame的加载
控制台输出报告:仅在开发者工具中打印违规日志,供调试参考
广告与统计完全恢复:AdSense、GTM、GA4、百度统计等全部正常
三、Nginx配置核心修改点
以下修改适用于所有站点,包括ZBlog、AnQiCMS、Go反向代理、Flask应用等。
3.1 CSP响应头名称变更
将强制执行的响应头改为报告模式:
# 修改前(会拦截广告) add_header Content-Security-Policy "default-src 'self'; script-src 'self' ..." always; # 修改后(仅报告不拦截) add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src * 'unsafe-inline' 'unsafe-eval'; ..." always;
3.2 script-src彻底放开
不再维护繁琐的Google域名白名单,直接使用通配符:
script-src * 'unsafe-inline' 'unsafe-eval';
这表示允许所有来源的脚本执行,包括动态加载的第三方广告脚本。
3.3 其他指令同步简化
style-src 'self' 'unsafe-inline' https://*.googleapis.com; img-src 'self' data: http: https:; font-src 'self' data: https://*.gstatic.com; connect-src 'self' http: https:; frame-src 'self' https:;
3.4 补齐缺失的安全头
在修改CSP的同时,为之前缺失安全头的站点统一补上:
server_tokens off; add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()" always;
四、宝塔面板保存注意事项
宝塔面板会对Nginx配置文件进行语法校验,其中对SSL区块的注释有严格的字符串匹配。以下两行注释绝对不能删除、修改或挪动位置,否则保存时会报错"请勿修改SSL相关配置中注释的404规则":
#SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则 #error_page 404/404.html;
同理,如果存在#ERROR-PAGE-START区块,也应保留其原始注释格式。
五、多站点统一配置模板
以下模板适用于PHP站点(ZBlog)、Go反向代理站点(VVCMS、AnQiCMS)以及Flask应用(GrabVid)。只需替换域名和证书路径即可复用。
server {
listen 443 ssl http2;
server_name example.com;
# ... root, index, SSL证书等配置 ...
#SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
#error_page 404/404.html;
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_tickets on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
error_page 497 https://$host$request_uri;
#SSL-END
server_tokens off;
#SECURITY-HEADERS-START
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()" always;
# CSP降级为Report-Only,彻底放开script-src,解决谷歌广告拦截
add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src * 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://*.googleapis.com; img-src 'self' data: http: https:; font-src 'self' data: https://*.gstatic.com; connect-src 'self' http: https:; frame-src 'self' https:; object-src 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests;" always;
#SECURITY-HEADERS-END
# ... 后续location、PHP处理、反向代理、缓存规则保持不变 ...
}六、验证与生效
修改完成后,通过以下步骤验证:
SSH执行
nginx -t检查语法,确保无conflicting server name等报错执行
nginx -s reload平滑重载配置浏览器访问站点,打开F12开发者工具,确认:
Network标签中广告脚本状态为200,无
(blocked:csp)Response Headers中包含
content-security-policy-report-only而非content-security-policyGoogle AdSense后台查看广告单元,确认展示恢复正常
七、总结
对于以内容展示和工具服务为主的站点,强制CSP的维护成本远高于安全收益。将Content-Security-Policy降级为Content-Security-Policy-Report-Only,并彻底放开script-src,是目前平衡广告变现与基础安全防护的最优解。同时补齐server_tokens、<X-Frame-Options等安全头,可在不破坏业务的前提下,维持合理的安全基线。