Nginx 的缓存配置主要分为两个层面,它们协同工作以最大化网站性能和减轻后端负载:
- 客户端缓存 (Browser Caching): 通过设置 HTTP 响应头,指示用户的浏览器缓存静态资源(如图片、CSS、JS)。
- 代理服务器缓存 (Proxy Server Caching): Nginx 作为反向代理,缓存后端应用服务器(如 Node.js, Java, Python 应用)的动态响应,后续相同请求可直接由 Nginx 返回,无需访问后端。
🌐 客户端缓存配置 (控制浏览器)
客户端缓存的核心是通过 Nginx 向浏览器发送特定的 HTTP 响应头,告知浏览器如何以及多久缓存资源。主要涉及 Cache-Control 和 Expires 头。
在 Nginx 中,我们通常使用 add_header 指令来精确控制这些头信息,或者使用 expires 指令进行快捷设置。
1. 静态资源长期缓存
对于不常变动的静态资源,如图片、字体、CSS/JS 文件,可以设置较长的缓存时间。特别是对于通过构建工具(如 Webpack, Vite)生成的、文件名中包含内容哈希(如 main.a1b2c3d4.js)的资源,可以设置非常激进的缓存策略。
server {
listen 80;
server_name example.com;
root /var/www/html;
# 场景1: 带哈希的静态资源 (推荐)
# 缓存1年,并标记为 immutable (不可变),浏览器在有效期内不会发起任何验证请求
location ~* "\.[a-f0-9]{8,}\.(css|js|png|jpg|jpeg|svg|webp|ico|woff|woff2)$" {
add_header Cache-Control "public, max-age=31536000, immutable" always;
access_log off; # 可选:关闭日志以减少IO
}
# 场景2: 普通静态资源
# 缓存30天,允许浏览器和CDN缓存
location ~* \.(css|js|png|jpg|jpeg|gif|svg|webp|ico|woff|woff2)$" {
add_header Cache-Control "public, max-age=2592000" always;
access_log off;
}
}
2. HTML 文件缓存策略
HTML 文件(尤其是单页应用的入口 index.html)是应用的骨架,会频繁更新。因此,不应对其进行长期缓存,但可以避免每次都完全重新下载。
server {
# ... 其他配置
# 场景3: HTML 文件
# private: 只允许用户浏览器缓存,不允许CDN等中间代理缓存
# no-cache: 使用前必须向服务器验证资源是否更新
# must-revalidate: 缓存过期后,必须成功向源服务器验证后才能使用
location ~* \.html$ {
add_header Cache-Control "private, no-cache, must-revalidate" always;
}
}
注意 : 此策略依赖于 Nginx 自动提供的
ETag或Last-Modified响应头,浏览器会使用它们发起条件请求(如If-None-Match),服务器若判断未更新则返回304 Not Modified,从而节省带宽。
3. 禁用动态内容缓存
对于包含用户敏感信息或实时变化的动态内容(如API接口、用户个人资料页),必须禁用缓存,以防止信息泄露和数据不一致。
server {
# ... 其他配置
# 场景4: 动态内容/API
# no-store: 最严格的指令,指示浏览器和所有中间代理不得缓存任何内容
location /api/ {
add_header Cache-Control "no-store" always;
# ... 代理到后端应用的配置
}
}
强制缓存和协商缓存
强制缓存 是"直接用本地缓存,不找服务器";协商缓存是"先问服务器缓存还能不能用,能用就用,不能用再下载"。
- 强制缓存
强制缓存是性能最优的策略。一旦配置生效,浏览器在有效期内直接使用本地缓存,完全不与服务器进行任何网络交互。
核心机制
- 响应头: 由
Cache-Control和Expires控制。- 状态码: 命中时,浏览器控制台显示
200 (from disk cache)或200 (from memory cache)。- 优先级:
Cache-Control的优先级高于Expires(如果同时存在)。Nginx 配置示例
通常用于图片、CSS、JS 等不常变动的静态资源。
location ~* \.(jpg|jpeg|png|gif|css|js|woff2|woff)$ { # 1. 设置缓存有效期为 1 年 (31536000 秒) # 2. public: 允许所有用户(包括 CDN)缓存 # 3. immutable: 告诉浏览器文件在有效期内不会变,刷新也不会检查更新(极致优化) add_header Cache-Control "public, max-age=31536000, immutable" always; # 或者使用 expires 指令(效果等同于设置 max-age 和 Expires) # expires 1y; }常见指令详解
指令值 含义 max-age=3600缓存有效时间为 3600 秒。 public允许浏览器和中间代理(如 CDN)缓存。 private只允许浏览器缓存,不允许 CDN 缓存。 no-cache跳过强制缓存,直接进入协商缓存阶段(每次都要问服务器)。 no-store禁止一切缓存,每次都从服务器下载(用于敏感数据)。
- 协商缓存
当强制缓存过期(或设置为
no-cache)时,浏览器会向服务器发起请求,询问资源是否更新。这就是协商缓存。核心机制
- 响应头: 由
Last-Modified/ETag控制。- 请求头: 浏览器会带上
If-Modified-Since或If-None-Match进行验证。- 状态码:
- 未修改: 返回
304 Not Modified,浏览器继续使用本地缓存。- 已修改: 返回
200 OK和新资源。两种对比策略
策略 依据 缺点 优先级 基于时间 Last-Modified(服务端)If-Modified-Since(客户端)精度只有秒级;如果文件修改时间变了但内容没变,也会重新下载。 较低 基于哈希 ETag(服务端)If-None-Match(客户端)消耗服务器性能(需计算文件哈希)。 较高 注意: 如果同时存在
ETag和Last-Modified,服务器会优先根据ETag进行判断。Nginx 配置示例
Nginx 默认会自动根据文件属性生成
ETag和Last-Modified,通常无需显式配置,但你可以手动开启或调整。
location / { # 强制缓存失效,每次都协商 add_header Cache-Control "no-cache, must-revalidate" always; # 开启 ETag (默认开启) etag on; # 开启 Last-Modified (默认开启) # last_modified on; }
两种缓存的配合流程
在实际应用中,这两种缓存通常是配合工作的。流程如下:
- 首次访问: 浏览器请求资源,Nginx 返回
200+ 资源内容 +Cache-Control: max-age=60+ETag。- 60秒内再次访问: 命中强制缓存,直接读取本地,不请求服务器。
- 60秒后再次访问: 强制缓存过期。浏览器发起请求,带上
If-None-Match: [ETag值]。- 服务器判断:
- 若文件未变:返回
304(空内容)。浏览器更新缓存有效期,继续使用旧文件。- 若文件已变:返回
200+ 新文件 + 新ETag。
Nginx 最佳实践配置
针对不同类型资源,采用不同的缓存策略:
server { listen 80; server_name example.com; # --- 策略 1: 静态资源 (带哈希指纹的文件) --- # 适用于 main.a1b2.js 这种文件名带哈希的资源 # 策略:强缓存 1 年,永不协商 location ~* \.[a-f0-9]{8,}\.(js|css|png|jpg)$ { add_header Cache-Control "public, max-age=31536000, immutable" always; access_log off; } # --- 策略 2: 普通静态资源 --- # 适用于 logo.png 这种文件名不变的资源 # 策略:强缓存 30 天 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { add_header Cache-Control "public, max-age=2592000" always; } # --- 策略 3: HTML 文件 --- # 适用于 index.html # 策略:不做强缓存,每次必须协商 (no-cache) # 确保用户能第一时间获取最新的 JS/CSS 入口 location ~* \.html$ { add_header Cache-Control "private, no-cache, must-revalidate" always; } # --- 策略 4: 动态 API 接口 --- # 策略:禁止缓存 location /api/ { add_header Cache-Control "no-store" always; proxy_pass http://backend; } }
- 强制缓存 (
Cache-Control: max-age):性能最高,用于静态文件。- 协商缓存 (
ETag/Last-Modified):用于验证文件是否更新,用于 HTML 或频繁变动的资源。- 组合拳 :HTML 使用
no-cache(协商缓存),引用的 JS/CSS 使用max-age=31536000(强缓存)并配合文件名哈希,是前端工程化的标准做法。
🔄 代理服务器缓存配置 (缓存后端响应)
代理缓存将后端应用的响应(通常是动态生成的)保存在 Nginx 服务器的磁盘上。当收到相同请求时,Nginx 可以直接从磁盘读取并返回,极大地减轻了后端负载,提升了响应速度。
配置代理缓存分为两步:定义缓存区和在 location 中启用。
1. 定义缓存区 (http 块)
在 nginx.conf 的 http 上下文中,使用 proxy_cache_path 指令定义缓存的存储路径、内存索引区和清理策略。
http {
# ... 其他 http 配置
# 定义一个名为 "my_cache" 的缓存区
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m use_temp_path=off;
server {
# ... 服务器配置
}
}
参数详解:
/var/cache/nginx: 缓存文件在磁盘上的存储路径。levels=1:2: 设置缓存目录的层级结构,避免单个目录下文件过多影响性能。keys_zone=my_cache:10m: 定义一个名为my_cache的共享内存区(10MB),用于存储缓存键和元数据(如访问次数、过期时间)。10MB 大约能存储 80,000 个键。max_size=1g: 磁盘上缓存文件的最大总容量。超出后,Nginx 会根据 LRU(最近最少使用)算法清理。inactive=60m: 如果缓存项在 60 分钟内未被访问,即使未过期也会被清理。use_temp_path=off: 建议关闭,让 Nginx 直接将临时文件写入缓存目录,避免不必要的文件拷贝,提升性能。
2. 在 location 中启用缓存
在需要缓存的 location 块中,使用 proxy_cache 等指令启用并细化缓存行为。
server {
listen 80;
server_name api.example.com;
location / {
# 启用缓存,使用上面定义的 "my_cache" 缓存区
proxy_cache my_cache;
# 定义缓存键,确保唯一性
# 这里将协议、主机、URI和查询参数组合作为键
proxy_cache_key "$scheme$host$request_uri";
# 针对不同后端响应状态码设置缓存时间
proxy_cache_valid 200 302 10m; # 成功响应缓存10分钟
proxy_cache_valid 404 1m; # 404响应缓存1分钟,避免缓存无效请求过久
# 添加响应头,便于调试缓存命中状态 (HIT/MISS)
add_header X-Cache-Status $upstream_cache_status;
# 高级配置:防止缓存雪崩
# 当缓存过期时,只允许一个请求回源更新,其他请求等待
proxy_cache_lock on;
# 在后台异步更新过期缓存,同时先给用户返回旧缓存
proxy_cache_background_update on;
# 在后端出错或超时时,允许返回过期缓存
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
# 代理到后端服务器
proxy_pass http://backend_server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
关键指令说明:
proxy_cache: 指定使用的缓存区名称。proxy_cache_key: 定义缓存的唯一标识。合理的键设计可以避免缓存碎片化。proxy_cache_valid: 定义不同响应码的缓存时长。X-Cache-Status: 通过添加此响应头,你可以用curl -I命令检查请求是命中缓存 (HIT) 还是未命中 (MISS)。proxy_cache_lock: 防止"缓存雪崩",即大量请求同时发现缓存过期并涌向后端。proxy_cache_background_update: 提升用户体验,用户能立即获得响应(即使是旧的),同时 Nginx 在后台无感更新缓存。
通过合理配置客户端缓存和代理服务器缓存,可以构建一个高效、高性能的 Web 服务架构。