nginx不控制http响应头发送顺序,关键在于header是否存在、值是否准确及是否被覆盖;应按资源类型精准配置cache-control,优先用add_header而非expires,并统一注入安全头、避免手动伪造content-type等关键头。

按资源类型设置 Cache-Control,别只靠 expires
静态资源更新频率不同,缓存策略必须区分对待:
-
带哈希/版本号的 JS/CSS(如 app.a1b2c3.js):可设长期缓存,推荐
add_header Cache-Control "public, max-age=31536000, immutable" -
未带哈希的 HTML、CSS、JS:建议短周期或验证式缓存,例如
add_header Cache-Control "no-cache, must-revalidate" -
图片、字体、图标等稳定资源:可用
expires 1y,它会自动设Cache-Control: max-age=31536000,但无法加public或immutable—— 此时优先用add_header - 避免在多个层级(http/server/location)重复设置同名缓存头,否则可能因覆盖逻辑导致意外行为
安全类 Header 要统一注入,慎用 location 局部覆盖
像 X-Content-Type-Options、X-Frame-Options、Strict-Transport-Security 这类头,必须对所有响应生效,且不能被子块意外清除:
- 在
server块顶层集中设置,加always参数确保透传到所有子 location - 例如:
add_header X-Content-Type-Options "nosniff" always; - 不要在某个
location ~ \.js$里单独加,否则 HTML 或图片响应可能漏掉 - 若上游(如 PHP-FPM)已输出这些头,
always可强制覆盖,避免空缺
Content-Type 和压缩头不能“手动伪造”
某些关键头由 Nginx 自动判断或条件生成,硬写 add_header 反而引发问题:
-
Content-Type应靠include mime.types;+ 文件后缀自动识别;手动设错(如把.woff2设成text/plain)会导致浏览器拒绝加载字体 -
Content-Encoding: gzip仅当启用gzip on且满足gzip_min_length等条件时才由 Nginx 自动添加;用add_header Content-Encoding gzip是无效甚至有害的 -
Vary头(如Vary: Accept-Encoding)由gzip_vary on自动管理;若再手动add_header Vary User-Agent,Nginx 会合并为Vary: Accept-Encoding, User-Agent,顺序不可控,但值是正确的
验证比配置更重要:用 curl 看真实响应
别只信配置文件,每次改完都要实测:
- 用
curl -I https://yoursite.com/style.css查看实际返回的响应头 - 检查是否有重复的
Cache-Control、缺失的Content-Type、错误的Content-Encoding - 浏览器开发者工具 Network 面板中点开资源,看 Headers → Response Headers 是否符合预期
- 特别注意:如果用了反向代理(如 CDN 或 Nginx 前置),需确认是源站 Nginx 还是中间层在加头,避免多层叠加冲突











