You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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;
    }
}

关键逻辑解释:

  1. 路由与基础处理:vcl_recv里把API路径直接pass(不缓存),前端页面走缓存逻辑,同时处理客户端的验证头格式,确保后端能识别。
  2. 缓存持久化:vcl_backend_response里设置TTL让Varnish保留缓存内容,同时强制Cache-Control规则,关闭grace模式保证每次都回源验证。
  3. 强制回源验证:vcl_hit在缓存命中时,不管客户端有没有传验证头,都触发回源——如果客户端没传,就用缓存的ETag去后端验证;如果客户端传了,就用客户端的ETag验证(对应你的Phase C)。
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.20 06:59:30