使用Map配置Nginx Expires Header,页面重载时失效问题排查
问题描述
我有一个单页应用,不希望index.html文件被缓存(否则发布新版本后,用户浏览器会加载缓存的旧index.html,导致关联旧打包JS文件)。我配置的Nginx模板如下:
map $sent_http_content_type $expires { text/html epoch; default 1y; } server { listen $PORT; ... location / { add_header Access-Control-Allow-Origin *; try_files $uri /index.html; sendfile on; expires $expires; tcp_nopush on; gzip on; gzip_types text/html application/javascript text/css; } ... }
首次加载页面时,index.html的Expires头设置正确,但软重载页面时,Expires被设为1年。补充:首次加载/硬加载index.html时,响应头包含Content-Type:text/html,但软重载时该头消失。
问题原因
核心问题出在软重载时浏览器发送条件请求,Nginx返回304状态码的场景:
- 首次/硬加载时,服务器返回200状态码,响应头包含
Content-Type:text/html,map指令匹配到text/html规则,设置Expires: epoch(禁止缓存)。 - 软重载时,浏览器会带上
If-Modified-Since或If-None-Match这类条件请求头,Nginx判断文件未修改时返回304 Not Modified。此时响应头不会包含Content-Type这类实体头,导致$sent_http_content_type变量为空,map匹配到default规则,设置Expires: 1y。
解决方案
不要依赖$sent_http_content_type变量来控制index.html的缓存,直接针对index.html做单独配置,避免304场景下的变量失效问题:
方案1:单独配置index.html的location(推荐)
这种方式更高效,且符合Nginx的配置最佳实践:
# 单独处理index.html,强制禁止缓存 location = /index.html { expires epoch; add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; add_header Expires "0"; } location / { add_header Access-Control-Allow-Origin *; try_files $uri /index.html; sendfile on; expires 1y; # 其他静态资源缓存1年 tcp_nopush on; gzip on; gzip_types text/html application/javascript text/css; }
方案2:在原有location内判断请求URI
如果不想拆分location,可以通过判断请求URI来设置缓存规则(此场景下if的使用是安全的):
map $sent_http_content_type $expires { default 1y; } server { listen $PORT; ... location / { add_header Access-Control-Allow-Origin *; try_files $uri /index.html; sendfile on; tcp_nopush on; gzip on; gzip_types text/html application/javascript text/css; # 针对index.html强制禁止缓存 if ($request_uri = /index.html) { expires epoch; add_header Cache-Control "no-cache, no-store, must-revalidate"; add_header Pragma "no-cache"; add_header Expires "0"; } # 其他资源使用默认缓存规则 if ($request_uri != /index.html) { expires $expires; } } ... }
内容的提问来源于stack exchange,提问作者Spring Memory
相关产品推荐
相关产品推荐

