Varnish 有时(最多约 5% 的请求)会触发 err_too_many_redirects,在 docker 中与 plesk for wordpress 结合运行

Varnish 有时(最多约 5% 的请求)会触发 err_too_many_redirects,在 docker 中与 plesk for wordpress 结合运行

我们已遵循这些说明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-ProtoVary标题将确保 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删除并坚持使用。CookieVaryVary: 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标头中。这将防止陷入重定向循环。

相关内容