Nginx 反向代理返回 304 是故障吗?先别急着“修”
很多站长在配置完 Nginx 反向代理后,查看访问日志时,发现满屏的 304 Not Modified 状态码。第一反应往往是:“是不是我的服务器出问题了?” 或者 “是不是被攻击了?”
其实,304 状态码不仅不是错误,反而是浏览器与服务器之间一次非常高效的“默契配合”。它表示客户端(如浏览器)已经缓存了资源的最新版本,服务器确认该缓存依然有效,无需重新传输完整内容。这能极大地节省带宽、降低服务器负载,尤其是在图片、CSS、JS 等静态资源请求量巨大的场景下。
但是,如果 304 出现的场景不对劲,比如动态接口也频繁返回 304,或者你希望强制刷新缓存,那问题就来了。本文不整虚的,直接带你从原理到实操,彻底掌握 Nginx 反向代理下 304 的来龙去脉。
一、理解 304 的核心机制:Last-Modified 与 ETag
在反向代理环境中,Nginx 作为“中间人”,负责转发请求和响应。304 的产生,主要依赖于两个 HTTP 头:
- Last-Modified / If-Modified-Since:服务器告诉浏览器资源最后修改时间,浏览器下次请求时带上这个时间,服务器对比后决定是否返回 304。
- ETag / If-None-Match:服务器给资源一个唯一标识符(通常是内容哈希),浏览器请求时带上,服务器比对标识符是否一致。
在 Nginx 反向代理场景下,Nginx 需要正确传递这些头信息。如果配置不当,比如 Nginx 自己生成了 ETag,但后端服务器(如 Tomcat、Node.js)也生成了 ETag,就可能导致验证失败,从而绕过缓存,每次都返回 200 全量数据——这才是真正的性能隐患。
二、反向代理中 304 频繁出现的三大常见原因
1. 静态资源缓存策略生效
这是最正常的情况。如果你的 Nginx 配置了 expires 指令或 Cache-Control 头,浏览器会在有效期内直接使用本地缓存(返回 200 from cache)。当缓存过期后,浏览器会发起条件请求,若文件未变动,Nginx 直接返回 304,告诉浏览器“继续用你本地那份”。
这种情况完全无需干预,反而说明你的缓存策略是健康的。
2. 代理层缓存未命中,但源站验证通过
Nginx 反向代理配置了 proxy_cache 时,如果缓存过期,Nginx 会向后端发起请求。如果后端返回的响应头包含 Last-Modified 或 ETag,Nginx 在缓存过期后,会向后端发起条件请求(带上 If-Modified-Since),后端发现资源没变,返回 304,Nginx 则更新缓存时间并复用旧缓存。
这属于正常的缓存回源验证流程。
3. 客户端强制刷新或代理软件干扰
用户按 Ctrl+F5 强制刷新时,浏览器会发送 Cache-Control: no-cache,此时 Nginx 必须回源验证。如果源站响应头未正确配置,Nginx 可能会每次都返回 200,导致性能下降。另外,一些安全软件或浏览器插件也可能篡改请求头,导致 304 异常。
三、Nginx 反向代理 304 的实战配置与调优
下面我们直接上干货,展示一套经过验证的 Nginx 反向代理配置,既能保证 304 正常生效,又能避免“假 304”问题。
1. 基础反向代理配置(保留缓存验证头)
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend_server;
# 关键:传递客户端的条件请求头给后端
proxy_set_header If-Modified-Since $http_if_modified_since;
proxy_set_header If-None-Match $http_if_none_match;
# 传递后端的验证响应头给客户端
proxy_pass_header Last-Modified;
proxy_pass_header ETag;
# 标准代理头
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
关键点解析:很多默认配置会使用 proxy_pass_header 或 proxy_hide_header 来过滤响应头。如果误将 Last-Modified 或 ETag 隐藏,浏览器就无法进行条件验证,304 自然就不会出现,所有请求都会变成 200 全量返回,白白浪费流量。
2. 静态资源缓存策略(主动生成 304)
对于图片、CSS、JS 等静态文件,我们通常让 Nginx 直接处理,不转发给后端。此时可以主动开启缓存验证:
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable";
# 开启 ETag 生成(Nginx 默认开启)
etag on;
}
这样配置后,浏览器首次请求返回 200,30 天内直接使用本地缓存(200 from cache)。30 天后发起条件请求,如果文件没变,Nginx 返回 304,浏览器继续用旧缓存,同时刷新缓存时间。
3. 关闭不需要的 304(针对动态接口)
如果你的 API 接口不希望被浏览器缓存(比如用户信息、订单状态),需要显式禁止缓存验证:
location /api/ {
proxy_pass http://backend_api;
# 禁止缓存
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0";
add_header Pragma "no-cache";
# 移除验证头,强制每次都返回完整数据
proxy_hide_header Last-Modified;
proxy_hide_header ETag;
}
这样配置后,浏览器每次请求动态接口都会拿到 200 和最新数据,不会出现 304 导致的“数据不变”假象。
四、排查 304 异常的思路与工具
如果你发现 304 行为不符合预期,可以按以下步骤排查:
- 查看响应头:使用 Chrome DevTools 的 Network 面板,点击某个 304 请求,查看 Response Headers 中是否包含
Last-Modified或ETag。如果没有,说明 Nginx 在转发时丢弃了这些头。 - 检查 Nginx 配置:搜索
proxy_hide_header指令,确认没有误隐藏验证头。 - 测试后端响应:直接 curl 后端地址,查看返回的头信息是否包含验证头:
curl -I http://backend_server/resource - 观察缓存日志:如果配置了
proxy_cache,通过add_header X-Proxy-Cache $upstream_cache_status;可以查看缓存命中状态(HIT/MISS/BYPASS/EXPIRED)。
五、进阶:304 与代理缓存(proxy_cache)的协同
当 Nginx 配置了 proxy_cache 时,304 的行为会更复杂。Nginx 会优先使用自己的缓存,如果缓存过期,会向后端发起条件请求。此时需要确保后端支持 If-Modified-Since 验证,否则 Nginx 会收到 200 全量响应,导致缓存刷新成本高。
一个优化技巧是:为代理缓存设置较长的有效期,但配合 proxy_cache_revalidate 指令开启后台验证:
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m;
proxy_cache_revalidate on;
server {
location / {
proxy_cache my_cache;
proxy_cache_valid 200 60m;
proxy_cache_valid 404 1m;
proxy_pass http://backend;
}
}
proxy_cache_revalidate on 会让 Nginx 在缓存过期后,先向后端发送条件请求。如果后端返回 304,Nginx 会更新缓存时间并直接复用旧缓存,无需重新下载完整内容。这对于大文件(如视频、安装包)的缓存效率提升非常明显。
六、聊点实在的:服务器选型与性能的关系
处理 304 逻辑本身对 CPU 消耗极小,但如果你的服务器配置太低,频繁的磁盘 I/O(读取缓存文件、写日志)也会拖慢响应速度。我们之前专门写过一篇关于云服务器测评分数高,网站却卡成狗?测评与调优的正确打开方式 的文章,里面详细讲了为什么跑分高不代表体验好,以及如何真正定位瓶颈。
另外,如果你用的是 WordPress 等动态程序,304 只能解决静态资源的问题,动态页面的性能优化需要更系统的方案。可以参考我们整理的WordPress 性能优化加速实战清单,里面有不少可以直接落地的技巧。
七、总结:304 不是洪水猛兽,而是性能助手
回到最初的问题:Nginx 反向代理出现 304,到底是好是坏?答案是:在绝大多数场景下,304 是健康的表现,说明你的缓存机制正在高效工作。只有当动态接口也出现 304 导致数据不更新时,才需要针对性处理。
最后给各位站长一个建议:与其纠结 304 状态码,不如把精力放在更合理的缓存策略设计上。记住,缓存是 Web 性能的第一生产力。同时,选择一台配置合理、I/O 性能靠谱的云服务器也至关重要,毕竟再好的缓存策略也需要硬件来支撑。如果你正在纠结服务器选型,不妨看看我们之前写过的别再把“跑分”当“调优”!云服务器测评与性能调优,压根是两码事,避免花冤枉钱。
希望这篇文章能帮你彻底搞懂 Nginx 反向代理中的 304 状态码。如果你在配置过程中还有疑问,欢迎在评论区留言讨论。