1. 为什么需要配置Nginx安全头?
作为一名运维工程师,我经常遇到这样的场景:网站功能一切正常,但安全扫描报告却亮起了红灯。最常见的问题就是缺少必要的HTTP安全响应头。这些看似简单的配置,实际上是Web应用安全的第一道防线。
HTTP安全头的工作原理很简单:它们告诉浏览器应该如何安全地处理你的网页内容。比如是否允许被嵌入iframe、哪些资源可以加载、是否强制HTTPS连接等。没有这些头信息,浏览器就会采用默认行为,而这往往会给攻击者可乘之机。
在实际工作中,我发现很多团队会花大价钱购买WAF(Web应用防火墙),却忽略了这些免费且高效的基础安全措施。这就像给房子装了最贵的防盗门,却忘记关窗户一样。通过合理配置Nginx的安全头,我们可以防御:
- XSS(跨站脚本攻击):通过CSP头限制脚本来源
- 点击劫持:通过X-Frame-Options防止页面被恶意嵌入
- MIME类型混淆攻击:通过X-Content-Type-Options阻止浏览器错误解析文件
- 中间人攻击:通过HSTS强制HTTPS连接
- 信息泄露:通过Referrer-Policy控制referer信息
2. 核心安全头详解与配置
2.1 Content-Security-Policy (CSP)
CSP是我认为最重要的安全头之一。它通过白名单机制控制资源加载,能有效阻止XSS攻击。下面是一个生产环境常用的配置:
nginx复制add_header Content-Security-Policy "default-src 'self';
script-src 'self' 'unsafe-inline' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https://*.example-cdn.com;
font-src 'self';
connect-src 'self' https://api.example.com;
frame-src 'none';
object-src 'none';
base-uri 'self';
form-action 'self';
report-uri https://example.com/csp-report" always;
关键参数解析:
default-src 'self':所有未明确指定的资源默认只允许从当前域加载script-src:特别注意'unsafe-inline'应该尽量避免,可以用nonce替代report-uri:配置违规报告收集端点,对调试非常有用
实际踩坑经验:
-
第三方资源处理:首次配置时经常因为漏掉某个CDN域名导致页面样式错乱。建议先用
Content-Security-Policy-Report-Only模式观察一段时间。 -
内联脚本问题:老系统往往有很多内联脚本。短期可以用nonce,长期应该重构为外部文件。例如:
nginx复制set $csp_nonce "random123";
add_header Content-Security-Policy "script-src 'nonce-$csp_nonce'";
- 动态内容加载:如果使用WebSocket或AJAX,记得在
connect-src中添加相应域名。
2.2 Strict-Transport-Security (HSTS)
HSTS告诉浏览器必须使用HTTPS连接,防止SSL剥离攻击。配置示例:
nginx复制add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
注意事项:
-
首次部署时不要立即设置很长的max-age,可以先从几分钟开始测试。
-
preload标志表示申请加入浏览器预加载列表,提交后不可撤销。 -
如果子域名有独立证书,
includeSubDomains可能导致访问问题。
2.3 X-Frame-Options
这个头防止点击劫持攻击,控制页面是否可以被嵌入iframe:
nginx复制add_header X-Frame-Options "SAMEORIGIN" always;
选项说明:
DENY:完全禁止嵌入SAMEORIGIN:只允许同源页面嵌入ALLOW-FROM uri:已废弃,不建议使用
注意:现代浏览器更推荐使用CSP的frame-ancestors指令,两者同时存在时CSP优先级更高。
3. 其他关键安全头配置
3.1 X-Content-Type-Options
阻止浏览器MIME嗅探,强制使用声明的内容类型:
nginx复制add_header X-Content-Type-Options "nosniff" always;
这个头特别重要对于文件上传功能,可以防止浏览器将文本文件误执行为HTML/JS。
3.2 Referrer-Policy
控制Referer头信息,保护用户隐私:
nginx复制add_header Referrer-Policy "strict-origin-when-cross-origin" always;
常用策略:
no-referrer:完全不发送same-origin:同源时发送完整URLstrict-origin:跨域时只发送源(协议+域名)strict-origin-when-cross-origin:平衡安全与功能的推荐选项
3.3 Permissions-Policy
控制浏览器功能访问权限:
nginx复制add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()" always;
这个头特别适合不需要这些功能的网站,可以主动禁用减少攻击面。
4. 高级配置与优化
4.1 静态资源缓存策略
静态资源可以安全缓存,同时保持安全头:
nginx复制location ~* \.(css|js|png|jpg|jpeg|gif|ico|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header Content-Security-Policy "default-src 'self'";
add_header X-Content-Type-Options nosniff;
access_log off;
}
immutable告诉浏览器缓存内容永不过期,减少验证请求。
4.2 安全头的模块化配置
建议将安全头配置单独存放,方便维护:
nginx复制# /etc/nginx/conf.d/security-headers.conf
add_header Content-Security-Policy "...";
add_header Strict-Transport-Security "...";
# 其他头配置...
# 在server块中引入
include conf.d/security-headers.conf;
4.3 动态CORS配置
对于需要CORS的API接口:
nginx复制map $http_origin $cors_origin {
default "";
"~^https://(www\.)?example\.com$" $http_origin;
"~^https://app\.example\.com$" $http_origin;
}
server {
location /api/ {
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
}
}
5. 常见问题排查
5.1 安全头未生效
可能原因:
- 配置在错误的server或location块
- 有更高优先级的add_header指令覆盖
- 浏览器缓存了旧响应
检查方法:
bash复制curl -I https://example.com
5.2 CSP导致资源加载失败
调试步骤:
- 检查浏览器控制台错误信息
- 查看CSP违规报告(如果配置了report-uri)
- 临时改用
Content-Security-Policy-Report-Only
5.3 HTTPS混合内容问题
即使有HSTS,如果页面包含HTTP资源仍会被阻止。解决方法:
- 使用相对协议或直接HTTPS URL
- 配置Content-Security-Policy的
upgrade-insecure-requests
6. 生产环境完整配置示例
nginx复制# 安全头主配置
map $http_origin $cors_origin {
default "";
"~^https://(www\.)?example\.com$" $http_origin;
}
server {
listen 443 ssl http2;
server_name example.com;
# SSL配置
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# 安全头
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; upgrade-insecure-requests" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "geolocation=(), microphone=()" always;
# 静态资源
location ~* \.(css|js|png|jpg|jpeg|gif|ico|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# API接口
location /api/ {
add_header 'Access-Control-Allow-Origin' $cors_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://backend;
}
}
7. 安全头检测工具推荐
- SecurityHeaders.com:在线检测工具,给出评级和建议
- Mozilla Observatory:全面的安全扫描
- Chrome DevTools:Network面板查看实际响应头
- curl命令:快速检查头信息
bash复制curl -I https://example.com
配置完安全头后,建议用这些工具验证效果。安全是一个持续的过程,随着业务发展和新威胁出现,需要定期审查和更新这些配置。