直接用 map 指令按文件后缀设置缓存更清晰易维护,不依赖后端 Content-Type,而是通过 $uri 在请求初期匹配扩展名(如 .js、.jpg)映射缓存时间,HTML 设为 0 或 epoch 禁用,JS/CSS/字体设 1 年,图片设 1 周,需用 ~* 不区分大小写匹配并注意顺序防 default 拦截。

直接用 map 指令按文件后缀设置缓存,比写一堆 location 块更清晰、更易维护,关键是它不依赖后端返回的 Content-Type,而是从请求路径本身判断类型,适合纯静态服务或构建产物已带哈希的场景。
核心思路:用 $uri 匹配后缀,映射缓存时间
不同于基于 $sent_http_content_type 的方式(需等响应头生成),这里在请求刚进来时就根据 URL 路径决定缓存策略,响应前就确定 expires 值。
- 在
http块中定义 map,用正则匹配$uri的扩展名 - 为常见后缀分配合理过期时间:HTML 禁用、JS/CSS/字体设 1 年、图片设 1 周或 1 月
- 在
server或location中直接调用该变量:expires $expires_by_ext;
推荐配置示例
以下配置覆盖主流静态资源,兼顾安全与性能:
http {
map $uri $expires_by_ext {
default "epoch"; # 默认不缓存
~*\.(html|htm|php|xml)$ "0"; # HTML 类动态内容,强制不缓存
~*\.(js|css|woff2?|ttf|eot|svg|otf)$ "1y";
~*\.(jpg|jpeg|png|gif|webp|avif)$ "1w";
~*\.(ico|cur)$ "1m";
}
<pre class='brush:php;toolbar:false;'>server {
listen 80;
root /var/www/html;
location / {
expires $expires_by_ext;
# 若需加 immutable(仅限哈希文件),改用 add_header + always
add_header Cache-Control "public, immutable" always;
}
}}
注意:add_header 的 always 参数确保即使返回 304 或错误码也带上该头;immutable 仅对 URL 含哈希的资源有效,否则会锁死旧版本。
关键细节和避坑点
- 正则必须加
~*表示不区分大小写,避免.JPG或.CSS不匹配 - 匹配顺序重要:把具体后缀(如
\.js$)放前面,通配规则(如\..*$)放最后,否则会被 default 拦截 -
"epoch"等效于Expires: Thu, 01 Jan 1970,浏览器立刻失效;"0"表示Expires头设为响应发出时刻,等效 no-cache - 若同时用
expires和add_header Cache-Control,前者会自动覆盖后者中的max-age,所以建议统一用add_header控制,expires仅作兼容补充
进阶:结合构建哈希做精准控制
如果项目使用 Webpack/Vite,输出文件带内容哈希(如 main.a1b2c3.js),可进一步细化:
- 对含哈希的 JS/CSS:设
"1y"+immutable - 对不含哈希的同名文件(如
common.js):降级为"1h"或"1d" - 用
~*\.(js|css)(\?.*)?$匹配带查询参数的请求,但注意:Nginx 默认不将 query string 用于缓存键,除非显式配置


















