varnish 中的 ESI 不像浏览器中的 iframe 或链接标签那样工作,因为它不会连接到您提供的任何 url。 ESI 只是在 varnish 中启动一个新请求并完成工作流程(vcl_recv 等)。
你期望varnish像一个http客户端一样,解析url,设置授权标头,将主机标头设置为 api.dev:8081 并启动一个新的http连接/请求,但它不会。在这种情况下,我的猜测是它会启动一个新的请求,并将 req.url 设置为 /app.php/next 继承父资源请求的标头(包含 esi 标签),或者可能完全忽略 esi 标签。
完成你想做的事情的方法是(在 vcl_recv 中):
if (req.esi_level > 0 && req.url == "/app.php/next") {
set req.http.Authorization = "BASIC [base64 encoded admin:adminpass]"
return (pass);
}
然后esi标签应该看起来像<esi:include src="/app.php/next" />
如果您需要 ESI 请求到达不同的后端服务器,则需要将该服务器添加为不同的命名后端:
backend authorization_needed {
.host = "api.dev";
.port = "8081";
}
在 vcl_recv 中,告诉 varnish 将其用于 esi 请求:
if (req.esi_level > 0 && req.url == "/app.php/next") {
set req.http.Authorization = "BASIC [base64 encoded admin:adminpass]"
set req.backend = authorization_needed;
return (pass);
}
如果后端响应与“api.dev”不同的虚拟主机,您可能还需要在该 if 块中设置 req.http.Host。
Update:
由于基本授权来自客户端,并且当存在 req.http.Authorization 时您正在调用 return (pass),因此 varnish 将不会 ESI 处理这些页面。您必须在 vcl_fetch() 中显式启用 esi,当您通过时不会调用该函数。
因此,要传递 ESI 片段的授权但不传递父页面的授权,请更改 vcl_rev:
if (req.http.Authorization && req.esi_level == 0) {
set req.http.X-Esi-Authorization = req.http.Authorization;
unset req.http.Authorization;
}
else if (req.http.X-Esi-Authorization && req.esi_level > 0 ) {
set req.http.Authorization = req.http.X-Esi-Authorization;
return (pass);
}
并添加到vcl_fetch:
if (req.http.X-Esi-Authorization) {
set beresp.do_esi = true;
}
最终效果是父响应是可缓存的并且将处理 esi,esi 片段本身将始终与客户端的授权标头一起传递到后端。