nginx 简单实践:Web 缓存【nginx 实践系列之三】
〇、前言
本文为 nginx 简单实践系列文章之二,主要简单实践了两个内容:正向代理、反向代理,仅供参考。
关于 Nginx 基础,以及安装和配置详解,可以参考博主过往文章:
https://www.cnblogs.com/hnzhengfy/p/Nginx.html
一、强制缓存、协商缓存
1.1 强制缓存(强缓存)
强制缓存指的是当用户请求一个资源时,浏览器直接从本地缓存中读取数据而不向服务器发送任何请求。
这种缓存机制主要依赖于 HTTP 响应头中的 Cache-Control 和 Expires 字段。
Expires:这是 HTTP/1.0 引入的一个头部字段,它指定了资源在该时间之前被认为是有效的。例如,Expires: Wed, 21 Oct 2025 07:28:00 GMT。但是,由于 Expires 依赖于客户端的时间设置,可能出现非预期的状况。
Cache-Control:HTTP/1.1 引入了更为强大的 Cache-Control 头部,提供了更多控制缓存行为的指令,如下表:
值 | 含义 |
no-store | 禁止缓存(强缓存和协商缓存),客户端不存储任何值 |
no-cache | 禁止强缓存,需要重写验证(可以理解为禁止强缓存,启用协商缓存) |
private | 私有缓存,禁止中间人(比如 CDN 等代理缓存) |
public | 共享缓存,允许中间人缓存 |
max-age | 资源可以被缓存的最大时间,单位:秒,是一个相对时间,优先级高于 Expires,示例(缓存两分钟):max-age=120 |
s-maxage | 用于共享缓存,单位:秒,如果在其有效期内,不去访问 CDN 等。s-maxage 会覆盖 max-age 和 Expires |
must-revalidate | 缓存使用陈旧资源时,必需先验证状态 |
强缓存的优势:
- 减少网络请求:浏览器可以直接使用本地缓存中的资源,无需再次向服务器发起请求,从而减少了网络往返次数,提升了页面加载速度。
- 降低服务器负载:由于减少了对服务器的请求,服务器的压力也随之减小,有助于提高服务器的整体性能和稳定性。
- 节省带宽:避免了重复下载相同的数据,节省了用户的网络带宽,尤其对于移动设备或在带宽有限的情况下尤为重要。
- 提升用户体验:加速页面加载时间,尤其是对于静态资源如图片、CSS 和 JavaScript 文件等,能够显著改善用户体验。
强缓存的劣势:
- 可能展示过期内容:如果缓存策略设置不当,可能会导致用户看到过期的内容。例如,如果资源更新了但缓存尚未过期,用户将继续看到旧版本的资源,直到缓存失效。
- 缺乏灵活性:强制缓存的时间是预先设定好的,无法根据实际情况动态调整。这意味着即使服务器上的资源已经更新,只要客户端缓存未过期,用户就看不到最新的内容。
- 需要精确配置:正确配置缓存策略需要一定的专业知识,尤其是在处理不同类型的资源时(如静态资源与动态生成的内容),错误的配置可能导致缓存命中率低或频繁的缓存失效问题。
- 难以应对紧急更新:在某些情况下,比如发现安全漏洞或需要立即推送的重要更新,强制缓存可能会成为障碍,因为用户可能长时间看不到最新的修复或更新。
在实际应用中,通常会结合使用强制缓存和协商缓存,以达到既快速又可靠的缓存效果。例如,对于不经常变化的静态资源可以使用较长的缓存时间,而对于可能频繁更新的内容则采用较短的缓存时间或者使用协商缓存机制。
1.2 协商缓存
协商缓存(也称为验证缓存)是一种 HTTP 缓存机制,它在强制缓存失效后使用。
在这种情况下,浏览器会携带特定的缓存标识(如 ETag 或 Last-Modified)向服务器发送请求,询问资源是否有更新。如果资源没有变化,服务器将返回一个 304 Not Modified 状态码,指示客户端可以继续使用本地缓存;如果资源有变化,则服务器返回新的资源。
协商缓存主要依赖于 值标记法(ETag、If-None-Match)和比较最后修改时间法(Last-Modified、If-Modified-Since)两种头部字段来实现。
值标记法(ETag、If-None-Match):
- ETag:由服务器生成并返回给客户端的一个唯一标识符,通常基于文件内容的哈希值。每当资源发生变化时,ETag 也会随之改变。
- If-None-Match:当客户端再次请求资源时,会在请求头中带上之前获取的 ETag 值。服务器收到请求后,会比较当前资源的 ETag 与请求中的 If-None-Match 值。如果两者相同,说明资源未变,服务器返回 304 Not Modified(未更改,缓存可用);否则,服务器返回新的资源及其新的 ETag。
比较最后修改时间法(Last-Modified、If-Modified-Since):
- Last-Modified:服务器告诉客户端资源最后修改的时间。例如,Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT。
- If-Modified-Since:客户端在后续请求中会带上这个时间戳。服务器接收到请求后,检查资源的最后修改时间 Last-Modified 是否晚于 If-Modified-Since 指定的时间。如果是,服务器返回新资源;如果不是,服务器返回 304 Not Modified(未更改,缓存可用)。
协商缓存的优势:
- 确保数据新鲜度:通过与服务器进行验证,确保用户始终能够获取到最新的资源,避免了因缓存过期而看不到最新内容的问题。
- 节省带宽:如果资源没有变化,服务器只需返回一个小的 304 Not Modified 响应,而不需要重新传输整个资源,从而节省了带宽。
- 灵活性:相比强制缓存,协商缓存提供了更高的灵活性,允许根据实际资源的变化情况动态调整缓存策略。
协商缓存的劣势:
- 增加网络开销:即使资源没有变化,每次请求都需要与服务器进行一次验证,这增加了少量的网络开销。
- 复杂性较高:需要正确设置和管理 ETag、Last-Modified 等头部字段,增加了配置和维护的复杂性。
- 性能略逊于强缓存:虽然节省了带宽,但相比直接从缓存中读取资源,协商缓存仍需要一次网络往返,因此性能稍逊一筹。
通过合理地结合使用强制缓存和协商缓存,可以根据不同的需求优化网站的性能和用户体验。
对于不常变化的静态资源,可以优先考虑使用强制缓存;而对于那些需要保持最新状态的资源,则应采用协商缓存。
二、通过 nginx 配置实现 Web 缓存
2.1 强缓存的实现:Cache-Control
Expires 可能造成误差,因此不再进行示例测试。由于 Expires 配置是后端返回给客户端的一个时间戳,取自服务器时间,当客户端发送请求时,采用客户端的当前时间进行比较,如果两边时间有误差就会造成数据不一致的异常情况。
Cache-Control 的优先级高于 Expires,Expires 是 http1.0 的产物,而 Cache-Control 是 http1.1 的产物,两者同时存在的时候 Expire 会被 Cache-Control 的 max-age 覆盖,在不支持 http1.1 的情况下可能就需要 Expires 来保持兼容。
nginx 配置示例:
server { listen 8888; server_name www.testczzj.com; #charset utf-8; #charset koi8-r; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; location / { add_header Cache-Control max-age=500; # 缓存有效期配置为 500 秒 proxy_pass http://www.testczzj.com:5000; } }
如下图请求结果:(响应头中包含了强缓存配置项 Cache-Control)
如下图针对文件 bootstrap.min.css 的二次请求详情:
2.2 协商缓存实现:Cache-Control:no-cache
客户端每次请求都会向服务端发起,若没更新则返回 304,若有更新就正常返回 200。
nginx 配置,其实就是修改了 Cache-Control 这个配置项:
server { listen 8888; server_name www.testczzj.com; #charset utf-8; #charset koi8-r; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; location / { add_header Cache-Control no-cache; # 配置为协商缓存 proxy_pass http://www.testczzj.com:5000; } }
如下图,首次加载是从服务器拉取全部数据,待第二次刷新时,在服务端数据未发生变更的情况下,返回 304,效率大大提高:
如下图针对文件 bootstrap.min.css 的二次请求详情:
2.3 无缓存:Cache-Control:no-store
nginx 配置如下:
server { listen 8888; server_name www.testczzj.com; #charset utf-8; #charset koi8-r; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; location / { add_header Cache-Control no-store; # 配置无缓存 proxy_pass http://www.testczzj.com:5000; } }
每次请求都是从服务端取数据,如下图:
参考:https://blog.csdn.net/JarryNeverGiveup/article/details/131060939

本文来自博客园,作者:橙子家,欢迎微信扫码关注博主【橙子家czzj】,有任何疑问欢迎沟通,共同成长!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步