说到 Nginx,大家聊得最多的往往是高并发、反向代理、负载均衡、平滑升级这些性能向的话题。但我这几年帮别人排查线上问题,发现一个更常见也更扎眼的现象:很多站点的 Nginx 响应头几乎是裸奔的——没有 X-Content-Type-Options,没有 CSP,没有 HSTS,甚至连 Server 版本号都挂在外面。说白了,安全头这一块完全没人管。
这篇文章想系统聊聊 Nginx 常用安全头:每个头到底解决什么问题、在 Nginx 里怎么配、实际部署会踩哪些坑。适合刚接触 Web 安全的开发运维,也适合想把现有 Nginx 配置补全的同学。我会把配置模板直接贴出来,你复制过去改改就能用,但更希望你看完明白每一行的含义,别成了无脑抄配置。
1. 安全头到底在防什么
1.1 浏览器端的“安检员”
HTTP 响应头里那些看似不起眼的字段,其实扮演着浏览器安检员的角色。浏览器拿到 HTML、JS、图片这类资源后,要不要执行、能不能进 iframe、请求有没有带 Referer、连接能不能降级成明文,这些判断很大程度都取决于响应头里那几个安全开关。
举个例子,如果没有 X-Content-Type-Options: nosniff,浏览器有时候会自作聪明地去“嗅探”文件真实类型。一个被上传的文本文件,如果内容长得像 HTML,浏览器就可能把它当页面渲染,这种 MIME 混淆经常被用来绕过上传检测。再比如少了 X-Frame-Options 或 CSP 的 frame-ancestors,你的支付页面、后台管理页就可能被恶意站点用 iframe 套进去,配合透明遮罩做点击劫持,用户点了哪里自己都不知道。
安全头做的事情,是把这类原本交给浏览器判断的“概率题”变成“送分题”。你明确告诉浏览器:不许乱猜类型、不许有人套我页面、不信任任何外部来源的脚本。浏览器就乖乖照做,攻击面自然就小了。
1.2 为什么都在 Nginx 这层配
很多框架自带安全头配置,比如 Express 里的 helmet、Spring Security 的 headers 配置。但如果你用的是 Nginx 做网关或反代,我强烈建议在 Nginx 这一层统一处理。
原因很直接:Nginx 是流量出口,所有响应都要从这里经过。不管后面挂的是 Tomcat、PHP-FPM 还是 Node.js,也不管是静态页面还是接口返回,只要在 Nginx 配好安全头,就能一刀切覆盖所有响应。上游应用如果有自己的安全头,Nginx 的 add_header 还能和它们共存,主动权在你手里。
不过这里要留个心眼:Nginx 的 add_header 存在继承陷阱,后面有专门一节放雷。很多站点配置了半天只剩一两个头生效,多半就是踩了继承规则的坑。
2. 常用安全头逐个拆解
先放一张速查表,后面再逐个展开:
| 安全头 | 核心作用 | 常用取值 |
|---|---|---|
| X-Content-Type-Options | 禁止 MIME 类型嗅探 | nosniff |
| X-Frame-Options | 禁止页面被 iframe 嵌入 | DENY / SAMEORIGIN |
| X-XSS-Protection | 旧浏览器 XSS 过滤器 | 1; mode=block |
| Content-Security-Policy | 内容来源白名单 | default-src 'self' ... |
| Strict-Transport-Security | 强制 HTTPS 访问 | max-age=31536000 |
| Referrer-Policy | 控制 Referer 携带策略 | strict-origin-when-cross-origin |
| Permissions-Policy | 限制浏览器功能权限 | camera=(), geolocation=() |
| Cache-Control | 控制页面/接口缓存 | no-store / no-cache |
| server_tokens | 隐藏 Nginx 版本号 | off |
2.1 X-Content-Type-Options:一个字节都不能错
这个头的配置极其简单,就一行:
add_header X-Content-Type-Options "nosniff" always;它告诉浏览器:不要主动猜测资源的 Content-Type,服务端说是什么就是什么。这能堵住一类很隐蔽的问题——存储型 XSS 或恶意文件上传后,本来应该以纯文本或图片返回,如果少了 nosniff,浏览器可能会把内容当 HTML 去解析,脚本就跟着执行了。
我见过一个实际案例:某个文件上传功能只校验了扩展名,攻击者上传了一个后缀是.txt、内容却带着