
浏览器访问经过CDN加速的网站时,有时会在开发者工具中看到 304 Not Modified。它不是报错,也不表示CDN没有返回内容。304的意思是:客户端已有一份缓存副本,服务器确认这份副本仍然有效,因此不再重复发送响应正文。
这个过程通常由 ETag 或 Last-Modified 完成。两者都能减少重复传输,但判断依据、精度和配置方式不同。如果源站、CDN和浏览器之间的校验头处理不一致,可能出现内容更新后仍显示旧页面、每次请求都回源,或者同一资源在不同节点表现不一致。
304 Not Modified是怎么产生的?
第一次请求资源时,服务器通常返回 200 OK、完整正文和缓存响应头:
HTTP/1.1 200 OK Cache-Control: public, max-age=300 ETag: "page-v18" Last-Modified: Wed, 26 Aug 2026 02:10:00 GMT
浏览器保存资源后,在缓存过期或需要重新验证时,会把上次收到的校验信息带回服务器:
GET /assets/app.js HTTP/1.1 If-None-Match: "page-v18" If-Modified-Since: Wed, 26 Aug 2026 02:10:00 GMT
如果资源没有变化,服务器可以返回304。这个响应没有正常的资源正文,浏览器继续使用本地副本,并更新缓存状态。它节省了下行流量,但仍会产生一次网络往返和请求处理开销。
ETag与Last-Modified有什么区别?
ETag按资源版本判断
ETag 是服务器为某个资源版本生成的标识,可以来自内容哈希、版本号、文件元数据或应用自己的规则。客户端重新验证时,通过 If-None-Match 把标识发回去。
ETag更适合内容变化频繁、动态页面没有可靠修改时间、短时间内可能连续生成多个版本的场景。ETag必须与最终响应版本绑定,不能只根据固定URL生成。
ETag分为强校验和弱校验。强ETag表示两个资源在字节层面一致;弱ETag通常以 W/ 开头,表示内容在语义上等价,但字节表示可能不同。普通缓存重新验证可以使用弱ETag,涉及范围请求等对字节一致性要求更高的场景则要谨慎。
Last-Modified按修改时间判断
Last-Modified 记录资源最后修改时间。客户端通过 If-Modified-Since 发起条件请求,服务器比较当前修改时间后决定返回304还是完整的200响应。
它直观、兼容性好,静态文件服务器通常能自动生成。不过HTTP日期精度通常只到秒,文件时间也可能因为复制、部署或时钟差异发生变化,动态内容未必有可靠的“最后修改时间”。
客户端可能同时发送 If-None-Match 和 If-Modified-Since。按照HTTP语义,支持 If-None-Match 的服务器应优先使用它。两个校验器可以同时保留,但不能分别指向不同版本。
CDN在条件请求中做了什么?
条件验证可能发生在两个位置。
浏览器向CDN重新验证时,如果边缘节点已有可用缓存,并能根据缓存策略完成判断,CDN可以直接向浏览器返回304,不访问源站。
边缘缓存过期后,CDN也可能携带 If-None-Match 或 If-Modified-Since 请求源站。源站返回304,CDN继续使用已有对象并更新缓存有效期;资源已经变化时,源站应返回200和新正文,CDN再替换旧对象。
不同CDN产品对条件请求、压缩版本和响应头转发的处理并不完全相同。浏览器里看到一个304,不能直接证明它来自源站。还要结合CDN缓存状态头、Age 和日志判断。
内容更新了,为什么仍然返回304?
ETag没有随内容变化
应用更新了正文,却继续返回相同ETag,服务器就可能把新版误判为旧版。动态接口和服务端渲染页面最容易出现这种问题。
Last-Modified时间不准确
部署工具保留了旧文件时间,或者应用写入固定的 Last-Modified,都可能导致错误判断。还要检查服务器时钟;HTTP日期应使用GMT格式。
CDN仍保存旧的响应头
源站已经更新校验器,但边缘对象仍携带旧ETag或旧修改时间。应先查看 Age、缓存命中状态和节点信息,再决定是否刷新指定资源。不要一看到旧内容就直接清空全站缓存。
压缩版本共用错误的强ETag
同一资源可能有 gzip、Brotli 和未压缩版本。如果不同字节内容共用同一个强ETag,代理和客户端可能做出错误判断。需要一起检查 Content-Encoding、Vary: Accept-Encoding 和ETag生成规则。
Service Worker或应用缓存干扰
页面显示旧内容不一定是CDN造成的。Service Worker、浏览器强缓存、前端状态缓存都可能拦截请求。排查时先确认请求是否真的到达CDN。
如何排查304与缓存更新问题?
1. 查看首次响应
先用未缓存请求检查 Cache-Control、ETag、Last-Modified、Age、Vary,以及CDN提供的缓存命中或节点标识头。
2. 手动测试ETag
curl -I https://example.com/assets/app.js curl -I -H 'If-None-Match: "page-v18"' https://example.com/assets/app.js
第二次请求应根据当前资源版本返回304或200。测试时必须使用第一次响应中实际得到的ETag。
3. 手动测试修改时间
curl -I -H 'If-Modified-Since: Wed, 26 Aug 2026 02:10:00 GMT' https://example.com/assets/app.js
如果资源在该时间之后已经修改,却仍返回304,应检查文件时间来源、部署流程和中间缓存。
4. 比较源站与CDN响应
在有权限且不绕过安全控制的前提下,分别查看源站和CDN的ETag、Last-Modified、Cache-Control、Content-Encoding与Vary。源站正确而CDN错误,问题多半在缓存对象或边缘规则;源站本身就返回错误校验器,应先修复源站。
5. 修改资源后重新验证
做一次可控的小改动,记录修改前后的ETag、修改时间、状态码和正文哈希。只看状态码不够:200也可能返回旧正文,304也可能完全正确。
ETag和Last-Modified应该如何配置?
静态资源可以同时保留ETag与Last-Modified,但两者都要随真实版本变化。带内容哈希的文件名,例如 app.7f3c1a.js,通常可以设置较长缓存时间;内容变化后URL随之变化,对频繁重新验证的依赖会降低。
HTML和动态接口应按业务特点处理。公开且可缓存的HTML可以使用较短的 max-age 加重新验证;用户私有响应要设置合适的 private 或 no-store,不能为了减少请求就放入共享CDN缓存。
如果响应会随语言、压缩格式或请求头变化,要正确设置 Vary,并确认CDN缓存键与它一致。否则即使ETag正确,也可能验证到另一个版本的缓存对象。
304越多,CDN效果就越好吗?
不能只看304数量。304减少了响应正文,却没有消除请求和网络往返。对于带内容哈希、可以安全长期缓存的静态资源,直接使用未过期的本地副本通常比频繁重新验证更省请求。
HTML或更新频繁的配置文件需要及时检查版本,304则是合理结果。评估缓存策略时,应一起看缓存命中率、回源请求、下行流量、对象更新频率,以及用户看到新内容所需的时间。
常见问题
304会计入CDN请求次数吗?
通常仍会产生一次HTTP请求,因此可能计入请求量。是否回源取决于边缘节点能否完成验证以及缓存是否需要向源站重新验证。计费方式以所用CDN的当前规则为准。
使用ETag后还需要Cache-Control吗?
需要。Cache-Control 决定响应能否缓存、缓存多久和何时重新验证;ETag用于重新验证时判断内容是否变化。
no-cache是不是不缓存?
不是。no-cache 通常表示响应可以保存,但每次复用前必须重新验证。完全不希望存储敏感响应时,应评估 no-store。
清除CDN缓存能解决所有304问题吗?
不能。如果源站持续返回错误ETag、错误修改时间或错误缓存头,新对象仍会出现相同问题。
结论
304是一次成功的缓存重新验证。排查时要沿着浏览器缓存、CDN边缘缓存和源站校验器逐层核对。ETag应随资源版本变化,Last-Modified应来自可靠的修改时间,Cache-Control和Vary则决定缓存何时复用、何时验证,以及不同响应是否会混在一起。
CloudFlew提供基于AWS CloudFront的CDN服务。接入现有网站前,可以先整理静态资源、HTML和API各自的缓存要求,再在测试环境验证200、304和缓存刷新流程,避免用同一套规则覆盖所有内容。
参考资料
RFC 9110:HTTP Semantics,条件请求与304状态码:https://www.rfc-editor.org/rfc/rfc9110
MDN Web Docs:ETag:https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/ETag
MDN Web Docs:Last-Modified:https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Last-Modified
MDN Web Docs:304 Not Modified:https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/304
AWS CloudFront Developer Guide:Request and response behavior for custom origins:https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorCustomOrigin.html
资料核查日期:2026年8月26日。