Varnish 有時(最多約 5% 的請求)會觸發 err_too_many_redirects,在 docker 中執行並結合 plesk for wordpress
我們已經按照這些說明https://support.plesk.com/hc/en-us/articles/115001888894-Does-Plesk-support-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 並沒有通過每個請求(至少不是在損壞的請求上)。他指出
$$ another varnish domain $$在類似的請求中似乎很好地通過了這個記錄。 在對配置進行了一些仔細的比較之後,只有一件事讓我們印象深刻,那就是 plesk 中的首選域
$$ another varnish domain $$被設置為“無”,並且對於$$ the varnish domain $$被設置為$$ the varnish domain $$. 所以我們去了www。
$$ the varnish domain $$在我們的瀏覽器中,這似乎引發了問題。我們在這裡也將“首選域”切換為“無”,現在是 www。$$ the varnish domain $$重定向到$$ the varnish domain $$正好。 假設
似乎 plesk 自己的重定向忽略了“HTTP 的附加指令”,因此沒有添加“Vary: X-Forwarded-Proto”。我們將對此進行監控,並儘快發布更新,當我們完全確信這是原因時。
該問題可能與記憶體方面缺乏協議意識有關。很典型的是,應該重定向到 HTTPS 請求的 HTTP 請求最終會進入記憶體,這會無條件地將使用者重定向到 HTTPS 版本,即使他們實際請求的是 HTTPS 版本。
根據您使用的網路伺服器的類型,有多種方法可以解決此問題。
清漆日誌輸出
但在我們得出結論之前,我想看看一些
varnishlog
輸出。當重定向循環發生時,我希望看到以下命令的輸出:
varnishlog -g request -q "ReqUrl eq '/'"
假設這個問題也出現在我們作為 VSL 查詢添加到
varnishlog
命令的首頁上。我注意到你的
varnishncsa
輸出,但不幸的是它在輸出方面太有限了。varnishlog
對於調試來說要好得多,而varnishncsa
只是。檢驗假設
如果重定向循環確實是由於缺乏協議意識導致的,我們可以觸發如下問題:
- 執行
varnishadm ban obj.status "!=" 0
清空記憶體- 呼叫網站的純 HTTP URL 以確保記憶體此版本
- 呼叫網站的 HTTPS URL 以檢查您是否卡在重定向循環中
解決問題
如果所有測試加起來並且假設得到證明,那麼解決方案實際上非常簡單。
如果您使用 Apache 作為網路伺服器,您可以將以下內容添加到您的
.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
您不僅可以看到命中,還可以斷定對像是由 transaction 插入到記憶體中的
886585
。雖然有一個
Vary
標題很好,但這個包含的Cookie
值是危險的。這意味著對於呈現給 Varnish 的每個可能的 cookie 值,都會在記憶體中儲存一個單獨的對象。這是非常危險的,所以請Cookie
從Vary
標題中刪除並堅持使用Vary: Accept-Encoding, X-Forwarded-Proto
.我查看的最後一筆交易是
* << Request >> 951082
. 它有一個X-Forwarded-Proto: https
請求標頭,不應導致 301 重定向,但不幸的是它確實如此。該
Hit
標籤公開了一些有趣的資訊:- Hit 951078 82.391648 10.000000 0.000000
被命中的對象最初是由事務插入記憶體中的
951078
。如果您密切關注,那是我們介紹的第一個。此事務是導致 301 重定向的僅 HTTP 事務。這正是返回的對象。因此,即使您請求的是 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
標題中。它將防止卡在重定向循環中。