如何在Varnish中通过ESI引入外部站点内容?
在Varnish 4.0.5中用ESI加载外部站点内容(无需逐个定义后端)
好问题!我之前在Varnish 4.x环境里处理跨站点ESI整合的时候也踩过一模一样的坑——内部路径正常,外部URL要么加载失败,要么静态资源全挂。咱们一步步来解决这个问题:
核心限制:Varnish ESI默认需要绑定后端
首先得明确:Varnish的<esi:include>标签默认只能指向预先定义的后端,不管是内部微服务还是外部站点。直接写外部URL(比如http://www.example.org/index.html)肯定会失败,因为Varnish不知道该把请求转发到哪个后端。但这不意味着你必须逐个硬编码所有外部站点的后端——我们可以用动态后端来解决。
解决方案:动态后端+URL代理+静态资源路径修正
1. 配置动态后端(无需逐个定义外部站点)
在VCL里通过directors模块创建动态后端池,根据请求头里的外部Host动态生成后端。这样不管你要引入多少个外部站点,都不用修改VCL里的后端定义。
示例VCL代码:
import directors; # 你的内部默认后端 backend internal_default { .host = "your-internal-microservice.com"; .port = "80"; } sub vcl_init { # 初始化一个轮询的动态后端池(也可以用其他调度策略) new dynamic_external_backends = directors.round_robin(); } sub vcl_recv { # 处理ESI的外部请求代理 if (req.url ~ "^/esi-proxy/" && req.http.X-ESI-External-Host) { # 根据请求头里的Host动态添加后端 set req.backend_hint = dynamic_external_backends.add_backend({ .host = req.http.X-ESI-External-Host; .port = "80"; # 如果是HTTPS,改成443,还要开启.ssl=true(注意Varnish开源版4.0默认不支持HTTPS后端,需额外处理) }); # 重写URL,去掉代理前缀,指向外部站点的实际路径 set req.url = regsub(req.url, "^/esi-proxy/", ""); # 对于外部站点请求,用pipe或pass,避免缓存混淆(根据需求调整) return (pipe); } # 常规内部请求处理 if (req.method != "GET" && req.method != "HEAD") { return (pass); } return (hash); }
2. 调整ESI标签的写法
把原来的外部URL改成指向我们的内部代理路径,同时通过自定义头传递外部站点的Host:
<esi:include src="/esi-proxy/index.html" alt="" onerror="continue">
(注:需要确保你的页面在Varnish处理时已经添加了X-ESI-External-Host头,比如在后端返回页面时注入,或者在VCL的vcl_recv/vcl_fetch里根据ESI路径判断添加)
3. 修复静态资源加载问题
这是你遇到的第二个坑:外部站点的静态资源用的是相对路径(比如/css/style.css),当ESI把内容插入到你的页面后,浏览器会向你的站点请求这些资源,而不是原外部站点。我们需要在VCL里把这些相对路径替换成绝对路径:
sub vcl_fetch { # 开启ESI解析 if (beresp.http.Content-Type ~ "text/html") { set beresp.do_esi = true; } # 修正外部站点内容里的静态资源路径 if (bereq.http.X-ESI-External-Host) { # 把所有src/href的相对路径替换成带外部Host的绝对路径 set beresp.body = regsuball(beresp.body, '(src|href)="/', '\1="http://' + bereq.http.X-ESI-External-Host + '/'); } }
4. 可选:优化缓存策略
为了避免不同外部站点的内容缓存混淆,建议在vcl_hash里加入外部Host作为缓存键的一部分:
sub vcl_hash { if (req.http.X-ESI-External-Host) { hash_data(req.http.X-ESI-External-Host); } hash_data(req.url); return (lookup); }
额外注意事项
- HTTPS外部站点:Varnish 4.0开源版默认不支持HTTPS后端,如果要引入HTTPS的外部站点,你需要:
- 编译Varnish时添加SSL支持(比较复杂);
- 或者在Varnish前面/后面加一个Nginx反向代理,专门处理HTTPS请求,Varnish再指向这个代理。
- 错误处理:ESI标签里的
onerror="continue"很重要,这样如果外部站点不可用,不会导致整个页面加载失败。
内容的提问来源于stack exchange,提问作者Mal
相关产品推荐
相关产品推荐

