NGINX 安全头配置¶
HTTP 安全头是防御 XSS、点击劫持和数据注入等客户端攻击的第一道防线。本指南涵盖了您应该在 NGINX 中配置的每个安全头。
关键:NGINX 中的头部继承
当您在子块(如 location)中使用 add_header 时,它会完全覆盖父块的所有头部。这是安全头配置错误的第一大原因。Gixy 可以自动检测此问题。
快速入门:必要的头部¶
将此块复制到您的服务器配置中以获得即时保护:
server {
listen 443 ssl http2;
server_name example.com;
# === 必要的安全头 ===
# HSTS - 强制 HTTPS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# 防止点击劫持
add_header X-Frame-Options "SAMEORIGIN" always;
# 防止 MIME 类型嗅探
add_header X-Content-Type-Options "nosniff" always;
# XSS 过滤器(旧版浏览器)
add_header X-XSS-Protection "1; mode=block" always;
# 控制 referrer 信息
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 内容安全策略(根据您的应用自定义)
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; frame-ancestors 'self';" always;
# 限制浏览器功能
add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=()" always;
# ... 您的其余配置
}
头部参考¶
1. Strict-Transport-Security (HSTS)¶
强制浏览器对您的域的所有未来请求使用 HTTPS。
防御的攻击: SSL 剥离、协议降级攻击
# 基本 - 1 年
add_header Strict-Transport-Security "max-age=31536000" always;
# 包含子域
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# 准备好加入预加载列表
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
| 指令 | 描述 |
|---|---|
max-age |
浏览器记住使用 HTTPS 的时间(秒) |
includeSubDomains |
应用于所有子域 |
preload |
有资格加入浏览器预加载列表 |
使用 includeSubDomains 之前
确保所有子域都有有效的 HTTPS。损坏的子域将完全无法访问。
推出策略
- 从
max-age=300(5 分钟)开始 - 增加到
max-age=86400(1 天) - 增加到
max-age=604800(1 周) - 最后设置
max-age=31536000(1 年)
Gixy 检查: hsts_header
2. Content-Security-Policy (CSP)¶
控制浏览器可以加载哪些资源。最强大的安全头。
防御的攻击: XSS、数据注入、点击劫持
# 严格 CSP(会破坏大多数网站 - 从新项目开始)
add_header Content-Security-Policy "default-src 'self'" always;
# 典型 Web 应用
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://api.example.com; frame-ancestors 'self';" always;
# 仅报告模式(用于测试)
add_header Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-report" always;
常用指令:
| 指令 | 控制内容 | 示例 |
|---|---|---|
default-src |
其他指令的回退 | 'self' |
script-src |
JavaScript 来源 | 'self' https://cdn.com |
style-src |
CSS 来源 | 'self' 'unsafe-inline' |
img-src |
图片来源 | 'self' data: https: |
font-src |
字体来源 | 'self' https://fonts.gstatic.com |
connect-src |
XHR、WebSocket、fetch | 'self' https://api.example.com |
frame-src |
iframe 来源 | 'self' |
frame-ancestors |
谁可以嵌入此页面 | 'self' |
base-uri |
基础 URL 限制 | 'self' |
form-action |
表单提交目标 | 'self' |
来源值:
| 值 | 含义 |
|---|---|
'self' |
同源 |
'none' |
全部阻止 |
'unsafe-inline' |
允许内联脚本/样式(尽量避免) |
'unsafe-eval' |
允许 eval()(避免) |
https: |
任何 HTTPS URL |
data: |
Data URI |
'nonce-{random}' |
带有匹配 nonce 的特定内联脚本 |
'sha256-{hash}' |
带有匹配哈希的特定内联脚本 |
CSP 开发工作流程
- 从
Content-Security-Policy-Report-Only开始 - 监控报告以识别所需的来源
- 逐步收紧策略
- 切换到强制执行的
Content-Security-Policy
3. X-Frame-Options¶
防止您的页面被嵌入 iframe(点击劫持保护)。
防御的攻击: 点击劫持、UI 重定向
# 拒绝所有框架
add_header X-Frame-Options "DENY" always;
# 仅允许同源
add_header X-Frame-Options "SAMEORIGIN" always;
# 允许特定来源(已弃用,改用 CSP frame-ancestors)
add_header X-Frame-Options "ALLOW-FROM https://trusted.com" always;
| 值 | 描述 |
|---|---|
DENY |
不能被任何人框架 |
SAMEORIGIN |
只能被同源框架 |
ALLOW-FROM uri |
已弃用,改用 CSP |
现代替代方案
CSP 的 frame-ancestors 指令更灵活且广泛支持:
add_header Content-Security-Policy "frame-ancestors 'self' https://trusted.com" always;
4. X-Content-Type-Options¶
防止 MIME 类型嗅探攻击。
防御的攻击: MIME 混淆攻击、驱动下载
add_header X-Content-Type-Options "nosniff" always;
只有一个有效值:nosniff。始终使用它。
5. X-XSS-Protection¶
启用浏览器内置的 XSS 过滤器。主要用于旧版浏览器。
防御的攻击: 反射型 XSS(在旧版浏览器中)
add_header X-XSS-Protection "1; mode=block" always;
| 值 | 描述 |
|---|---|
0 |
禁用过滤器 |
1 |
启用过滤器(净化) |
1; mode=block |
启用过滤器(阻止页面) |
现代浏览器
现代浏览器已弃用此头部,转而使用 CSP。但对 IE11 和旧版浏览器支持仍然有用。
6. Referrer-Policy¶
控制请求发送多少 referrer 信息。
防御的攻击: 信息泄露、隐私违规
# 推荐用于大多数网站
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 最大隐私
add_header Referrer-Policy "no-referrer" always;
# 仅同源
add_header Referrer-Policy "same-origin" always;
| 值 | 同源 | 跨源 (HTTPS→HTTPS) | 跨源 (HTTPS→HTTP) |
|---|---|---|---|
no-referrer |
无 | 无 | 无 |
same-origin |
完整 URL | 无 | 无 |
strict-origin |
仅来源 | 仅来源 | 无 |
strict-origin-when-cross-origin |
完整 URL | 仅来源 | 无 |
origin-when-cross-origin |
完整 URL | 仅来源 | 仅来源 |
7. Permissions-Policy(前身为 Feature-Policy)¶
限制对浏览器功能和 API 的访问。
防御的攻击: 不需要的功能访问、隐私违规
# 禁用所有敏感功能
add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()" always;
# 允许同源的特定功能
add_header Permissions-Policy "geolocation=(self), camera=(self), microphone=()" always;
# 允许特定来源的功能
add_header Permissions-Policy "geolocation=(self https://maps.example.com)" always;
常用功能:
| 功能 | 描述 |
|---|---|
geolocation |
GPS/位置访问 |
camera |
摄像头访问 |
microphone |
麦克风访问 |
payment |
Payment Request API |
usb |
USB 设备访问 |
fullscreen |
全屏 API |
autoplay |
媒体自动播放 |
display-capture |
屏幕捕获 |
8. 跨源头部¶
控制跨源资源访问。
Cross-Origin-Embedder-Policy (COEP)¶
# 所有资源都需要 CORS/CORP
add_header Cross-Origin-Embedder-Policy "require-corp" always;
# 无凭据模式
add_header Cross-Origin-Embedder-Policy "credentialless" always;
Cross-Origin-Opener-Policy (COOP)¶
# 隔离浏览上下文
add_header Cross-Origin-Opener-Policy "same-origin" always;
# 允许弹出窗口但隔离
add_header Cross-Origin-Opener-Policy "same-origin-allow-popups" always;
Cross-Origin-Resource-Policy (CORP)¶
# 只有同源可以加载此资源
add_header Cross-Origin-Resource-Policy "same-origin" always;
# 同站点可以加载
add_header Cross-Origin-Resource-Policy "same-site" always;
# 任何来源都可以加载
add_header Cross-Origin-Resource-Policy "cross-origin" always;
何时需要这些
这些头部是 SharedArrayBuffer 和高精度计时器等功能所必需的。大多数网站不需要它们。
CORS 配置¶
对于需要接受跨源请求的 API:
# 简单 CORS - 允许所有来源(仅限公共 API)
location /api/ {
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
if ($request_method = OPTIONS) {
return 204;
}
}
# 特定来源 CORS
location /api/ {
set $cors_origin "";
if ($http_origin ~* "^https://(www\.)?example\.com$") {
set $cors_origin $http_origin;
}
add_header Access-Control-Allow-Origin $cors_origin always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With" always;
add_header Access-Control-Max-Age "86400" always;
if ($request_method = OPTIONS) {
return 204;
}
}
永远不要将 * 与凭据一起使用
Access-Control-Allow-Origin: * 不能与 Access-Control-Allow-Credentials: true 一起使用。这是浏览器安全限制。
解决头部继承问题¶
问题¶
server {
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
location /api {
add_header Access-Control-Allow-Origin "*" always;
# X-Frame-Options 和 X-Content-Type-Options 现在消失了!
}
}
解决方案 1:使用 include 引入公共头部¶
# /etc/nginx/snippets/security-headers.conf
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;
server {
include /etc/nginx/snippets/security-headers.conf;
location /api {
include /etc/nginx/snippets/security-headers.conf;
add_header Access-Control-Allow-Origin "*" always;
}
}
解决方案 2:使用 headers-more 模块¶
# 在 http 级别设置不会被覆盖的头部
more_set_headers "X-Frame-Options: SAMEORIGIN";
more_set_headers "X-Content-Type-Options: nosniff";
解决方案 3:使用 map 实现动态头部¶
http {
map $uri $security_headers {
default "SAMEORIGIN";
~^/embed "ALLOW-FROM https://trusted.com";
}
server {
add_header X-Frame-Options $security_headers always;
}
}
Gixy 检查: Gixy 的 add_header_redefinition 会自动检测父块中定义的头部在子块中被意外清除的情况。
完整配置示例¶
http {
# === 全局安全头 ===
# 应用于所有服务器,除非被覆盖
# 注意:如果任何 location 使用 add_header,这些将被清除
# 使用 include 模式以保持一致性
server {
listen 443 ssl http2;
server_name example.com;
# SSL 配置
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# 在每个上下文中包含安全头
include /etc/nginx/snippets/security-headers.conf;
root /var/www/example.com;
index index.html;
location / {
include /etc/nginx/snippets/security-headers.conf;
try_files $uri $uri/ =404;
}
location /api/ {
include /etc/nginx/snippets/security-headers.conf;
# API 特定头部
add_header Access-Control-Allow-Origin "https://app.example.com" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE" always;
proxy_pass http://backend;
}
location /embed/ {
include /etc/nginx/snippets/security-headers.conf;
# 为嵌入端点覆盖框架策略
add_header X-Frame-Options "" always; # 清除默认值
add_header Content-Security-Policy "frame-ancestors https://partner.com" always;
}
}
}
/etc/nginx/snippets/security-headers.conf:
add_header Strict-Transport-Security "max-age=31536000; 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=(), camera=()" always;
测试您的头部¶
在线工具¶
- SecurityHeaders.com — 全面的头部分析
- Mozilla Observatory — 安全评估
- CSP Evaluator — CSP 策略分析
命令行¶
# 检查所有响应头
curl -I https://example.com
# 检查特定头部
curl -s -I https://example.com | grep -i "strict-transport"
# 详细输出检查
curl -v https://example.com 2>&1 | grep -i "< "
浏览器开发工具¶
- 打开开发工具 (F12)
- 转到 Network 标签
- 点击任何请求
- 查看 Response Headers
常见错误¶
1. 忘记 always¶
# 错误:仅在成功响应时添加
add_header X-Frame-Options "SAMEORIGIN";
# 正确:在所有响应(包括错误)时添加
add_header X-Frame-Options "SAMEORIGIN" always;
2. 在 API 上使用错误的 CSP¶
# 错误:带有 'self' 的 CSP 会阻止 API 响应
location /api/ {
add_header Content-Security-Policy "default-src 'self'" always;
}
# 正确:API 使用不同的或不使用 CSP
location /api/ {
# API 通常不需要 CSP
}
3. 在 HTTP 上使用 HSTS¶
# 错误:端口 80 上的 HSTS(无用且奇怪)
server {
listen 80;
add_header Strict-Transport-Security "max-age=31536000" always;
}
# 正确:HSTS 仅在 HTTPS 上
server {
listen 443 ssl;
add_header Strict-Transport-Security "max-age=31536000" always;
}
相关资源¶
加固 NGINX,使用维护的 RPM
使用 GetPageSpeed 提供的 NGINX Extras 在 RHEL/CentOS/Alma/Rocky 上获取持续更新的 NGINX 与模块。 了解更多.