我们已遵循这些说明https://support.plesk.com/hc/en-us/articles/115001888894-Plesk-support-Varnish-并能够使其“正常”运行,它通过 varnish 为 wordpress 网站提供服务,速度令人惊叹。一切都很好,但我们的正常运行机器人不时会报告短暂的停机时间(非常不规律,但每天 1 到 8 次,每次时间不到 5 分钟,全天不同时间)。
经过各种测试后,我们注意到 wp-admin url 存在问题,我们得出结论,这是因为我们的 DirectoryIndex(默认情况下为 index.html)导致的,我们通过将“DirectoryIndex index.php”添加到我们的“附加 Apache 指令”中解决了该问题。
有一件事对我们来说似乎很有趣,那就是当我们改变 PHP(FPM) 的提供方式时,它会影响相同的错误。当我们通过 NGINX 提供服务时,我们也会得到 err_too_many_redirects,如果我们通过 Apache 提供服务,它 95% 的时间都可以正常工作。
我在下面添加了一些(varnishncsa)日志记录,当标头响应状态为 301 时,我们就会进入循环:
172.17.0.1 - - [11/Jan/2022:13:54:55 +0000] "HEAD http://[the varnish domain]/ HTTP/1.0" 200 0 "https://[the varnish domain]" "Mozilla/5.0+(compatible; UptimeRobot/2.0; http://www.[uptimerobot].com/)"
172.17.0.1 - - [11/Jan/2022:13:56:51 +0000] "GET http://[the varnish domain]/ HTTP/1.0" 200 11184 "-" "-"
172.17.0.1 - - [11/Jan/2022:13:58:31 +0000] "HEAD http://[the varnish domain]/ HTTP/1.0" 301 0 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36"
172.17.0.1 - - [11/Jan/2022:13:58:32 +0000] "HEAD http://[the varnish domain]/ HTTP/1.0" 301 0 "http://[the varnish domain]" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36"
此后我们进行以下日志 8 次:
172.17.0.1 - - [11/Jan/2022:13:58:32 +0000] "HEAD http://[the varnish domain]/ HTTP/1.0" 301 0 "https://[the varnish domain]/" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36"
然后一个:
172.17.0.1 - - [11/Jan/2022:13:58:34 +0000] "GET http://[the varnish domain]/ HTTP/1.0" 301 238 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/96.0.4664.110 Safari/537.36"
然后7次:
172.17.0.1 - - [11/Jan/2022:13:59:55 +0000] "HEAD http://[the varnish domain]/ HTTP/1.0" 301 0 "https://[the varnish domain]" "Mozilla/5.0+(compatible; UptimeRobot/2.0; http://www.[uptimerobot].com/)"
然后22次:
172.17.0.1 - - [11/Jan/2022:14:00:17 +0000] "GET http://[the varnish domain]/ HTTP/1.0" 301 238 "https://[the varnish domain]" "Mozilla/5.0+(compatible; UptimeRobot/2.0; http://www.[uptimerobot].com/)"
此后,一切似乎又恢复正常。
更新:检查日志
@thijs-feryn 指出了一些有趣的事情,这让我们得出结论,我们太早地否定了一个旧理论。因为我们确信 X-Forwarded-Proto 被传递了,因为它在我们的“附加 apache 标头配置”中。
然而,在仔细阅读他的回复后,我们了解到 X-Forwarded-Proto 不知何故并未通过每个请求(至少不是在损坏的请求上)。他指出,[另一个 varnish 域] 似乎在类似的请求中很好地传递了此记录。
经过对配置进行仔细的比较后,我们发现只有一件事引起了我们的注意,那就是 plesk 中 [另一个 varnish 域] 的首选域设置为“无”,而 [varnish 域] 的首选域设置为 [varnish 域]。
因此我们在浏览器中访问了 www.[varnish 域名],这似乎触发了问题。我们在这里也将“首选域名”切换为“无”,现在 www.[varnish 域名] 可以正常重定向到 [varnish 域名]。
假设
似乎 Plesk 自己的重定向忽略了“HTTP 的附加指令”和因此没有添加“Vary: X-Forwarded-Proto”。我们将密切关注此事,当我们完全确信这是原因时,我们将尽快发布更新。
答案1
这个问题可能与缓存时缺乏协议意识有关。通常情况下,应该重定向到 HTTPS 请求的 HTTP 请求最终进入缓存,即使用户实际请求的是 HTTPS 版本,缓存也会无条件地将用户重定向到 HTTPS 版本。
根据您使用的网络服务器类型,有多种方法可以解决此问题。
varnishlog 输出
但在我们得出结论之前,我想看看一些varnishlog
结果。
当重定向循环发生时,我希望看到以下命令的输出:
varnishlog -g request -q "ReqUrl eq '/'"
假设该问题也发生在我们作为 VSL 查询添加到varnishlog
命令的主页上。
我注意到了你的
varnishncsa
输出,但不幸的是它的输出太有限了。对于varnishlog
调试来说要好得多,而varnishncsa
只是。
检验假设
如果重定向循环确实是由于缺乏协议感知导致的,我们可以按如下方式触发该问题:
- 运行
varnishadm ban obj.status "!=" 0
以清空缓存 - 调用网站的纯 HTTP URL 以确保此版本已被缓存
- 调用网站的 HTTPS URL 检查是否陷入重定向循环
修复问题
如果所有测试都正确并且假设被证实,那么解决方案实际上非常简单。
如果您使用 Apache 作为 Web 服务器,则可以将以下内容添加到文件中.htaccess
:
SetEnvIf X-Forwarded-Proto "https" HTTPS=on
Header append Vary: X-Forwarded-Proto
否则,您也可以将以下代码添加到您的 VCL 文件中:
sub vcl_backend_response {
if(beresp.http.Vary) {
if(beresp.http.Vary !~ "X-Forwarded-Proto") {
set beresp.http.Vary = set beresp.http.Vary + ", X-Forwarded-Proto";
}
} else {
set beresp.http.Vary = "X-Forwarded-Proto";
}
}
添加X-Forwarded-Proto
到Vary
标题将确保 Varnish 在缓存中为 HTTP 和 HTTPS 创建单独的对象。
我还假设您的 TLS 代理实际上发送了一个
X-Forwarded-Proto
标头。
更新:检查日志
经过反复的评论,我收到了一些有见地的日志https://pastebin.com/QzPh1r5R这解释了发生了什么。
您** << BeReq >> 951078
可以看到X-Forwarded-Proto: http
标头。这意味着请求是通过纯 HTTP 请求发送的。此类请求应导致 301 重定向,并且根据以下日志行,它确实会导致重定向:
-- BerespProtocol HTTP/1.1
-- BerespStatus 301
-- BerespReason Moved Permanently
-- BerespHeader Date: Thu, 13 Jan 2022 08:55:17 GMT
-- BerespHeader Server: Apache
-- BerespHeader Location: https://[the varnish domain]/
-- BerespHeader Content-Length: 238
-- BerespHeader Content-Type: text/html; charset=iso-8859-1
-- TTL RFC 120 10 0 1642064118 1642064118 1642064117 0 0 cacheable
不仅重定向发生,还会缓存 120 秒。有趣的是,没有Vary: X-Forwarded-Proto
标头。
稍后,该* << Request >> 951080
交易会出现在日志中。我们看到以下请求标头:
- ReqHeader X-Forwarded-Proto: https
- ReqHeader Host: [another varnish domain]
因此,这是对另一个 Varnish 域的 HTTPS 请求,并且由于某种原因,它导致缓存命中并返回Vary
标头:
- RespHeader Vary: Accept-Encoding,Cookie,X-Forwarded-Proto
- VCL_call HIT
- Hit 886585 90003.966201 10.000000 0.000000
您不仅可以看到命中,还可以得出对象已通过事务插入到缓存中的结论886585
。
虽然有
Vary
标头是件好事,但这个标头包含的值很危险。这意味着,对于呈现给 Varnish 的每个可能的 cookie 值,缓存中都会存储一个单独的对象。这非常危险,因此请从标头中Cookie
删除并坚持使用。Cookie
Vary
Vary: Accept-Encoding, X-Forwarded-Proto
我查看的最后一个事务是* << Request >> 951082
。它有一个X-Forwarded-Proto: https
请求标头,不应该导致 301 重定向,但不幸的是它确实导致 301 重定向。
该Hit
标签暴露了一些有趣的信息:
- Hit 951078 82.391648 10.000000 0.000000
被命中的对象最初是通过事务 插入到缓存中的951078
。如果你仔细观察,就会发现这是我们介绍的第一个事务。此事务是仅 HTTP 事务,导致 301 重定向。
这就是返回的对象。因此,即使您请求的是 HTTPS 内容,您仍会被重定向,这就是您最终陷入无限循环的原因。
如果您查看交易返回的响应951082
,则看不到Vary
标题:
- RespProtocol HTTP/1.1
- RespStatus 301
- RespReason Moved Permanently
- RespHeader Date: Thu, 13 Jan 2022 08:55:17 GMT
- RespHeader Server: Apache
- RespHeader Location: https://[the varnish domain]/
- RespHeader Content-Length: 238
- RespHeader Content-Type: text/html; charset=iso-8859-1
- RespHeader X-Varnish: 951082 951078
- RespHeader Age: 37
- RespHeader Via: 1.1 varnish (Varnish/7.0)
结论:请确保已将其X-Forwarded-Proto
添加到您的Vary
标头中。这将防止陷入重定向循环。