基于ETag的内容重验证共享缓存的Varnish配置咨询
基于ETag的内容重验证共享缓存的Varnish配置咨询
嘿,你的需求完全合理——用Varnish实现共享缓存,每次请求都强制回源验证ETag,既节省后端渲染时间,又保证内容实时性。你的初始VCL太基础了,Varnish默认的缓存逻辑不会自动处理no-cache, must-revalidate这种强制验证的场景,得手动调整几个关键VCL子程序才能实现你的期望流程。
下面是适配你需求的完整Varnish配置,我会逐个部分解释作用:
vcl 4.0; backend default { .host = "localhost"; .port = "3000"; } backend api { .host = "localhost"; .port = "8000"; } sub vcl_recv { # 路由规则:API路径直接透传不缓存,前端页面走默认后端 if (req.url ~ "^/back/" || req.url ~ "^/_/") { set req.backend_hint = api; return(pass); } else { set req.backend_hint = default; } # 处理客户端的If-None-Match头,去掉首尾引号(部分客户端会带引号,后端需要纯ETag值) if (req.http.If-None-Match) { set req.http.If-None-Match = regsub(req.http.If-None-Match, "^\"|\"$", ""); } } sub vcl_backend_response { # 仅针对前端页面配置缓存规则 if (bereq.backend_hint == default) { # 设置缓存内容在Varnish中的保留时长(1小时,期间每次请求都会回源验证) set beresp.ttl = 1h; # 强制设置Cache-Control头,确保客户端和Varnish都遵循重验证规则 set beresp.http.Cache-Control = "public, no-cache, must-revalidate"; # 保留后端返回的ETag,用于后续验证 set beresp.http.ETag = beresp.http.ETag; # 关闭grace模式(不允许使用过期缓存),确保每次都必须回源验证 set beresp.grace = 0s; # 关闭keep缓存,避免残留旧内容 set beresp.keep = 0s; } } sub vcl_hit { # 缓存命中时,强制发起回源验证(符合must-revalidate要求) if (req.backend_hint == default) { # 如果客户端没传If-None-Match(比如首次请求的Client B),用缓存的ETag去后端验证 if (!req.http.If-None-Match) { set req.http.If-None-Match = obj.http.ETag; } # 触发回源请求 return(fetch); } } sub vcl_backend_fetch { # 回源时带上If-None-Match头,让后端能正确验证版本有效性 if (req.http.If-None-Match && bereq.backend_hint == default) { set bereq.http.If-None-Match = req.http.If-None-Match; } } sub vcl_deliver { # 处理后端返回304的场景:返回缓存的完整内容+200状态码(对应你的Phase B) if (obj.status == 304 && req.backend_hint == default) { set resp.status = 200; # 保留缓存中的关键响应头 set resp.http.Content-Length = obj.http.Content-Length; set resp.http.Content-Type = obj.http.Content-Type; set resp.http.ETag = obj.http.ETag; } }
关键逻辑解释:
- 路由与基础处理:
vcl_recv里把API路径直接pass(不缓存),前端页面走缓存逻辑,同时处理客户端的验证头格式,确保后端能识别。 - 缓存持久化:
vcl_backend_response里设置TTL让Varnish保留缓存内容,同时强制Cache-Control规则,关闭grace模式保证每次都回源验证。 - 强制回源验证:
vcl_hit在缓存命中时,不管客户端有没有传验证头,都触发回源——如果客户端没传,就用缓存的ETag去后端验证;如果客户端传了,就用客户端的ETag验证(对应你的Phase C)。 - 304转200:
vcl_deliver里把后端返回的304转换成200,同时返回缓存的完整内容,这样首次请求的客户端(比如Client B)能直接拿到页面,而不是空的304响应。
测试注意事项:
- 确保你的后端能正确处理
If-None-Match头:ETag匹配时返回304,不匹配时返回200新内容。 - 如果后端已经返回了正确的
Cache-Control头,你可以把vcl_backend_response里的set beresp.http.Cache-Control改成set beresp.http.Cache-Control = beresp.http.Cache-Control;,直接复用后端的头。 - 测试时可以用
curl模拟不同客户端的请求,比如:- 首次请求:
curl -v http://your-varnish-url/page - 带验证头的请求:
curl -v -H "If-None-Match: W/xxx" http://your-varnish-url/page
- 首次请求:
这样配置后,就能完美实现你描述的三个阶段流程了:共享缓存、每次回源验证ETag、根据后端响应返回缓存内容或新内容。
备注:内容来源于stack exchange,提问作者Xowap
相关产品推荐
相关产品推荐

