要将缓存存到客户端浏览器,关键是让 浏览器接收到合适的 HTTP 缓存控制响应头。这通常通过 add_header
添加控制头来实现。
✅ 一般做法(强缓存 + 协商缓存):
🔹 1. 强缓存:使用 Cache-Control
和 Expires
location /static/ {
root /path/to/static/files;
add_header Cache-Control "max-age=3600, public";
add_header Expires $http_expires;
}
max-age=3600
:缓存 3600 秒public
:任何缓存服务器或浏览器都可以缓存Expires
:指定绝对过期时间(可选)
🔹 2. 协商缓存:使用 ETag
和 Last-Modified
如果你在服务静态资源,比如:
location /static/ {
root /path/to/static/files;
etag on;
if_modified_since exact;
add_header Cache-Control "no-cache";
}
- 浏览器下次访问时会带
If-None-Match
或If-Modified-Since
,服务器可返回304 Not Modified
🧠 如果你是用 OpenResty 返回的 Lua 内容:
你可以手动添加缓存相关头部,例如:
ngx.header["Cache-Control"] = "max-age=60, public"
ngx.header["Expires"] = ngx.http_time(ngx.time() + 60)
ngx.say("hello world")
这样浏览器会在 60 秒内使用本地缓存,不会再请求服务端。
🧪 如何验证浏览器缓存是否生效?
方法一:Chrome F12 → Network → 观察响应头
状态码 200 + from memory cache 或 disk cache
状态码 304 表示协商缓存命中
响应头中应有:
Cache-Control
Expires
ETag
/Last-Modified
(如开启)
✅ 实战建议
静态资源使用强缓存:
location /assets/ {
root /usr/share/nginx/html;
add_header Cache-Control "max-age=31536000, public";
}
动态接口用协商缓存或短时间强缓存(配合版本号或参数):
location /api/data {
content_by_lua_block {
ngx.header["Cache-Control"] = "max-age=10"
ngx.say("data at ", ngx.now())
}
}
是不是看的云里雾里?我们来一步步详细讲解如何让 OpenResty/Nginx 将数据缓存到浏览器端(客户端),这样客户端再次访问时就不必重复请求。
🎯 目标
让浏览器缓存接口或静态资源内容,避免每次都请求服务器,提高性能。
🧱 基础概念先弄清楚
缓存类型 | 控制方式 | 浏览器行为 |
---|---|---|
强缓存 | Cache-Control + Expires |
浏览器 不发请求,直接用本地缓存 |
协商缓存 | ETag 或 Last-Modified |
浏览器 发请求,但服务器判断内容未变,返回 304,无内容体 |
✅ 示例一:配置强缓存(适用于静态资源)
location /static/ {
root /Users/yourname/openresty-static; # 静态文件目录
add_header Cache-Control "public, max-age=3600";
add_header Expires $http_expires;
}
解释:
max-age=3600
:缓存 3600 秒(1 小时)public
:允许任何缓存服务器(包括浏览器)缓存Expires
:兼容旧浏览器(用绝对时间)
🧪 测试方法:
- 浏览器访问一次
/static/test.js
- 再刷新,看 Network →
test.js
状态为200 from memory/disk cache
✅ 示例二:动态接口设置缓存头(Lua 模拟)
location /api/time {
content_by_lua_block {
ngx.header["Cache-Control"] = "public, max-age=30"
ngx.header["Expires"] = ngx.http_time(ngx.time() + 30)
ngx.say("server time: ", ngx.now())
}
}
解释:
- 设置响应头,告诉浏览器:30秒内请直接使用本地缓存
- 浏览器不会向服务器请求这段时间内的
/api/time
🧪 测试方法:
- 浏览器访问
/api/time
,记录时间 - 再访问一次,响应不变、Network 显示
from memory cache
,说明缓存生效
✅ 示例三:协商缓存(加上 ETag)
如果你服务静态文件,可以这样配置:
location /docs/ {
root /Users/yourname/openresty-docs;
etag on;
if_modified_since exact;
add_header Cache-Control "no-cache";
}
解释:
etag on
: Nginx 生成 ETag(唯一标识当前内容版本)no-cache
: 浏览器每次请求都会带If-None-Match
,服务器返回 304 表示内容没变
🧪 测试方法:
- 第一次请求
/docs/intro.html
→ 返回 200 - 第二次请求浏览器自动带上
If-None-Match
→ 返回 304 Not Modified
协商缓存(Conditional Requests)机制中,浏览器每次都会带上 ETag
或 Last-Modified
发起请求,以询问服务器是否资源有更新。
🔁 协商缓存工作机制(以 ETag
为例)
第一次请求:
浏览器访问
/example.js
服务器响应:
200 OK ETag: "abc123" Cache-Control: no-cache
浏览器会把这个 ETag 存起来。
第二次请求(浏览器再次访问):
浏览器自动带上头部:
If-None-Match: "abc123"
服务器比对资源的当前 ETag 与请求的
If-None-Match
:如果一样 → 资源没变,返回:
304 Not Modified
无内容体,浏览器用本地缓存展示。
如果不一样 → 资源已变,返回新的 200 和更新的内容与 ETag。
📦 注意:
- 你必须显式设置
Cache-Control: no-cache
,表示 “每次请求都要验证” - 如果你不设置,默认不会发起协商请求,可能只靠
max-age
强缓存
🚧 注意事项
- 接口不宜长期缓存,可以短时间缓存如 5~60 秒,用于频繁请求但数据变化不快的情况。
- 静态资源可以长期缓存,但要结合文件名加 hash,如
main.abcd1234.js
- 如果你用 Vue/React 构建的页面,也可以通过
nginx.conf
给/dist
设置缓存头
📦 常见 Cache-Control 组合参考
场景 | 配置示例 | 说明 |
---|---|---|
浏览器强缓存 | Cache-Control: public, max-age=86400 |
一天内直接使用本地缓存 |
协商缓存 | Cache-Control: no-cache + ETag |
每次请求,服务器可返回304 |
不缓存 | Cache-Control: no-store |
浏览器不保存任何内容 |
CDN 缓存,浏览器不缓存 | Cache-Control: public, max-age=86400, must-revalidate |
浏览器每次都向服务器确认 |
题外话:
CDN 缓存(Content Delivery Network 缓存)是指将网站的静态资源(如图片、JS、CSS、HTML等)缓存在离用户更近的边缘节点服务器上,以提升访问速度、减轻源站压力、提升系统可用性。
🧠 一句话理解:
CDN 缓存就是:
“让用户访问 CDN 边缘节点的缓存副本,而不是每次都去请求源站”。
✅ 需要注意的是边缘节点是 CDN 服务商提供的,不是你自己买的服务器。
🧠 什么是边缘节点?
边缘节点(Edge Node)是 CDN 服务商在全国各地、甚至全球布置的缓存服务器。这些服务器靠近用户,称为“边缘”。
比如你用了阿里云 CDN,当上海的用户访问你的网站时,请求不会直接打到你自己的源站服务器,而是访问阿里云在上海部署的 CDN 节点。
📦 谁提供这些边缘节点?
你不需要自己买,常见 CDN 服务商都提供大量边缘节点:
CDN 服务商 | 边缘节点说明 |
---|---|
阿里云 CDN | 全国几百个节点,支持全球覆盖 |
腾讯云 CDN | 国内+海外分布式节点 |
百度云加速 | 全国部署节点 |
七牛云 CDN | 国内主打图床/加速 |
Cloudflare | 全球超过 300+ 城市节点 |
AWS CloudFront | 全球 CDN 服务,适合国际化 |
Fastly / Akamai | 国际大厂,专注北美和全球 |
🖼️ 举个例子帮助你理解
你的网站部署在北京阿里云的一台服务器上,用户可能来自全国各地:
用户地理位置 | 没有 CDN | 有 CDN |
---|---|---|
北京 | 快(直连) | 更快(边缘节点) |
广州 | 慢(跨区访问) | 快(广州本地 CDN 节点) |
上海 | 慢(跨区) | 快(上海本地节点) |
美国 | 非常慢(跨境) | 快(洛杉矶/SF 节点) |
🛠️ 如何使用这些边缘节点?
你将资源部署在源站(比如你的服务器、对象存储等)
你通过 CDN 服务商的加速域名对外访问,比如:
原地址:https://yourserver.com/logo.png CDN 加速地址:https://cdn.yourdomain.com/logo.png
用户访问的是 CDN 域名,CDN 会自动在边缘节点缓存和分发。
💡 总结一句话:
CDN 的边缘节点是 CDN 平台提供的,分布在各地,你无需部署也无需购买,只要开通使用即可享受加速服务。
🗂️ 缓存的内容类型
CDN 常缓存以下内容:
资源类型 | 是否常缓存 |
---|---|
图片(.jpg/.png/.gif) | ✅ |
JS/CSS/字体文件 | ✅ |
HTML 页面(根据配置) | ✅(需配置) |
接口数据(JSON 等) | ✅(需配置) |
视频/音频资源 | ✅ |
🏗️ CDN 缓存原理
用户访问资源(如
https://cdn.example.com/logo.png
)CDN 边缘节点查找本地缓存:
- ✅ 找到了(命中缓存):直接返回。
- ❌ 没有(未命中缓存):回源到源站,取回资源 → 缓存在边缘节点 → 返回用户。
下一次其他用户访问时就直接用缓存了。
🔐 缓存控制方式(CDN 依据这些控制)
CDN 判断是否缓存/是否过期主要依赖:
响应头 | 说明 |
---|---|
Cache-Control |
是否缓存、缓存多久(如 max-age=600 ) |
Expires |
指定过期时间(已被 Cache-Control 替代) |
ETag /Last-Modified |
协商缓存用,做回源时判断是否更新 |
Vary |
用于不同 User-Agent/语言生成不同缓存副本 |
👉 CDN 可以配置“强缓存”或“协商缓存”,也可结合自身规则(如 URL 后缀、参数名等)自定义策略。
🚀 CDN 缓存的好处
优势 | 描述 |
---|---|
⚡ 提升访问速度 | 用户从就近节点获取资源,降低延迟 |
🔁 降低源站压力 | 静态内容不再频繁请求源站 |
💥 提高抗压能力 | 多节点分发请求,防止高峰宕机 |
🌍 跨地域加速 | 国内外访问都能快 |
🔧 示例:设置缓存响应头(Nginx)
location /static/ {
root /var/www/html;
expires 1h; # 设置缓存1小时
add_header Cache-Control "public";
}
✅ 小结
问题 | 答案 |
---|---|
CDN 缓存和本地缓存一样吗? | 类似,都是减少重复请求。但 CDN 是服务器层缓存 |
CDN 缓存在哪? | 缓存在 CDN 边缘节点(距离用户近的服务器) |
什么时候会回源? | 缓存未命中,或者缓存过期时 |
📌 总结
- 想让客户端缓存数据,关键是设置好 HTTP 响应头
- OpenResty 本身不自动帮你做浏览器缓存,你要显式加
Cache-Control
、Expires
、ETag
等头 content_by_lua
里也能加ngx.header[...]
发送这些头部