导读:本期聚焦于罗经纬创作的《Nginx返回304 Not Modified的缓存控制原理是什么?深入理解协商缓存机制》,敬请观看详情。浏览器第二次请求同一资源时,常常会收到304 Not Modified响应而没有拿到完整内容,这背后其实是Nginx协商缓存在起作用。本文从HTTP缓存的整体流程讲起,剖析Last-Modified与ETag两套校验机制的实现原理,说明If-Modified-Since和If-None-Match请求头如何与Nginx配合完成缓存协商,并对比两种方案的适用场景与精度差异。文中还会给出expires、etag等常用指令的配置示例,分析304与强缓存的区别,帮助你在静态资源优化和CDN部署中正确控制缓存行为,减少不必要的流量传输。

当浏览器再次请求一个已经访问过的静态资源时,抓包工具里经常能看到一个状态码为304的响应——服务器没有返回文件内容,浏览器却正确显示了资源。这就是HTTP协商缓存在工作。Nginx作为最常用的静态资源服务器,对这套机制的支持非常完善,理解它的内部原理,对于优化网站加载速度、减少带宽消耗都有直接帮助。这篇文章从HTTP缓存模型入手,把304的来龙去脉讲清楚。

Nginx返回304 Not Modified的缓存控制原理是什么?深入理解协商缓存机制

一、HTTP缓存的两级模型:强缓存与协商缓存

要理解304,先要明白浏览器缓存是分层工作的。第一层是强缓存,由Cache-ControlExpires响应头控制。当强缓存有效时,浏览器直接从本地磁盘读取资源,根本不会向服务器发请求,开发者工具里会显示200 (from disk cache)或类似的提示。这一层完全不涉及Nginx的参与。

第二层才是协商缓存,也就是产生304的那一层。当强缓存过期后,浏览器会带着缓存标识信息(文件修改时间或内容指纹)去询问服务器:我手里的这个版本还能用吗?如果服务器判断资源没有变化,就返回304 Not Modified,响应体为空;如果变了,就返回200和完整的新内容。可以看到,304省掉的是响应体的传输,请求本身还是要发的。

两级缓存配合的逻辑是:强缓存追求零请求,协商缓存追求零传输。一个常见的策略是对带哈希指纹的资源(如app.a3f9c2.js)设置超长的强缓存时间,对不带指纹的资源(如index.html)关闭强缓存、只走协商缓存,这样既能保证更新及时,又能最大化命中缓存。

二、Last-Modified与If-Modified-Since:基于时间的校验

Nginx默认会对静态文件开启基于修改时间的协商缓存。第一次请求时,Nginx返回的响应头里会带上Last-Modified: Wed, 15 May 2024 08:30:00 GMT,这是文件在操作系统层面的mtime。浏览器把这个值记下来,下次请求时通过If-Modified-Since请求头原样带回。Nginx收到后与当前文件的mtime比较:如果一致,直接返回304,跳过文件内容发送;如果不一致,返回200和新文件。

这套机制的实现依赖Nginx的静态文件模块,判断逻辑非常轻量,只是字符串解析加时间比较,对服务器几乎没有额外开销。但它有一个先天缺陷:时间精度只有秒。如果文件在一秒内被修改了多次,mtime可能没变,浏览器会继续使用旧版本。另一个问题是被touch过的文件,内容没变但mtime变了,会导致一次无意义的重新下载。如果确实需要禁用这个头,可以在配置中关闭:

location /static/ {
    # 关闭Last-Modified头的输出
    last_modified off;
}

一般来说不建议关闭,除非资源内容由上游动态生成,时间戳没有参考意义。还需要注意,If-Modified-Since只在GET或HEAD请求中有效,POST请求不会触发协商缓存,这是HTTP协议层面的规定,与Nginx无关。

三、ETag与If-None-Match:基于内容的校验

ETag是比Last-Modified更精确的校验方式,本质是资源内容的指纹。Nginx默认生成的ETag格式为"_mtime-文件大小",例如"6644e418-1a2b",前半部分是十六进制的修改时间戳,后半部分是十六进制的文件大小。浏览器第二次请求时通过If-None-Match头带上这个值,Nginx对比后决定返回304还是200。

当响应头里同时存在ETag和Last-Modified时,浏览器的协商顺序是先看If-None-Match。只有ETag匹配成功才返回304;如果ETag不匹配,即使If-Modified-Since能匹配上,也照样返回200。也就是说ETag的优先级更高,这解决了秒级精度的问题——只要内容或大小变了,指纹就会变。

使用ETag有一个经典的坑需要注意:多服务器部署时,如果开启了etag并且使用了inode相关信息,不同机器上的同一个文件会生成不同的ETag,导致缓存永远失效。不过从Nginx 1.3.3版本开始,默认的ETag格式已经不再包含inode,这个问题基本消失了。如果想显式控制,可以这样配置:

location /assets/ {
    root /var/www;
    # 开启ETag(默认已开启,通常无需显式写)
    etag on;
    # 强缓存1年,配合文件名哈希使用
    add_header Cache-Control "public, max-age=31536000";
}

四、典型场景配置与常见误区

来看一个前后端分离项目的经典配置。HTML入口文件需要及时更新,所以关闭强缓存,只依赖协商缓存;JS、CSS等带哈希指纹的静态资源则放心设置超长强缓存:

server {
    listen 80;
    server_name example.ipipp.com;

    location = /index.html {
        root /var/www/dist;
        # 关闭强缓存,每次都协商
        add_header Cache-Control "no-cache";
        etag on;
    }

    location ~* \.(js|css|png|jpg|woff2)$ {
        root /var/www/dist;
        # 指纹资源缓存一年
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

这里的no-cache经常被误解为不缓存,实际上它的含义是每次使用前必须向服务器协商验证,协商通过后依然可以复用本地缓存。真正不缓存的是no-store。而immutable是告诉浏览器,即使强缓存过期了也不要在刷新时发起协商请求,适合内容永不过期的指纹资源。

另一个误区是把304当成万能优化。304虽然省了响应体,但请求和响应头还是要传输,HTTP连接建立、TLS握手等开销依然存在。对于高频访问的小资源,304带来的节省很有限,更好的做法是通过强缓存或HTTP/2连接复用来减少请求本身。排查304问题时,可以先用curl验证:

# 第一次请求,拿到Last-Modified和ETag
curl -I http://example.ipipp.com/app.js

# 第二次请求,带上协商头观察是否返回304
curl -I -H "If-Modified-Since: Wed, 15 May 2024 08:30:00 GMT" \
     -H 'If-None-Match: "6644e418-1a2b"' \
     http://example.ipipp.com/app.js

总结一下:304是Nginx与浏览器之间的一次缓存协商结果,核心机制是Last-Modified加If-Modified-Since的时间校验,以及ETag加If-None-Match的内容校验,两者都由Nginx静态文件模块自动处理。实际优化中,正确区分强缓存与协商缓存的适用对象,为指纹资源设置长缓存、为入口文件保留协商能力,才能把缓存体系的价值发挥到最大。

Nginx缓存304 Not Modified协商缓存修改时间:2026-09-06 00:18:52

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260906/51238.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。