如何使用 Varnish 加速我的網站
由於 Symfony 的快取使用標準 HTTP 快取標頭,HTTP 快取 可以替換為任何其他反向代理。Varnish 是一個強大、開放原始碼的 HTTP 加速器,能夠快速提供快取內容,並包含對 邊緣端包含 的支援。
讓 Symfony 信任反向代理
Varnish 會自動將 IP 以 X-Forwarded-For
轉發,並在請求中保留 X-Forwarded-Proto
標頭。如果您未將 Varnish 設定為受信任的代理,Symfony 會將所有請求視為來自 Varnish 主機的不安全 HTTP 連線,而不是真正的用戶端。
請記得在您的前端控制器中呼叫 Request::setTrustedProxies() 方法,以便將 Varnish 視為受信任的代理,並使用 X-Forwarded-* 標頭。
路由和 X-FORWARDED 標頭
為了確保 Symfony Router 使用 Varnish 正確產生 URL,必須存在 X-Forwarded-Port
標頭,Symfony 才能使用正確的埠號。
此埠號對應於您的設定用於接收外部連線的埠(80
是 HTTP 連線的預設值)。如果應用程式也接受 HTTPS 連線,則可能會有另一個代理(因為 Varnish 本身不做 HTTPS)在預設 HTTPS 埠 443 上處理 SSL 終止,並使用 X-Forwarded-Proto
標頭將請求作為 HTTP 請求轉發到 Varnish。在這種情況下,您需要新增以下組態片段
1 2 3 4 5 6 7
sub vcl_recv {
if (req.http.X-Forwarded-Proto == "https" ) {
set req.http.X-Forwarded-Port = "443";
} else {
set req.http.X-Forwarded-Port = "80";
}
}
注意
在使用反向代理或負載平衡器時強制使用 HTTPS 需要正確的組態,以避免無限重新導向迴圈;請參閱 如何在負載平衡器或反向代理後方設定 Symfony 以正常運作 以取得更多詳細資訊。
Cookie 和快取
預設情況下,當請求隨附 Cookie 或基本身份驗證標頭 時,大多數快取代理不會快取任何內容。這是因為頁面的內容應該取決於 Cookie 值或身份驗證標頭。
如果您確定後端永遠不會使用 Session 或基本身份驗證,請讓 Varnish 從請求中移除相應的標頭,以防止用戶端繞過快取。實際上,您至少需要在網站的某些部分使用 Session,例如,當使用具有 CSRF 保護 的表單時。在這種情況下,請確保 僅在實際需要時才啟動 Session,並在不再需要時清除 Session。或者,您可以研究 快取包含 CSRF 保護表單的頁面。
在 JavaScript 中建立且僅在前端使用的 Cookie,例如來自 Google Analytics 的 Cookie,仍然會傳送到伺服器。這些 Cookie 與後端處理無關,不應影響快取邏輯。為了確保這一點,請設定您的 Varnish 快取以 清理 Cookie 標頭,方法是僅保留必要的 Cookie(例如,Session Cookie)並移除所有其他 Cookie。這樣一來,當沒有活動 Session 時,頁面也可以被快取。
如果您使用預設設定的 PHP,則 Session Cookie 通常命名為 PHPSESSID
。此外,如果您的應用程式依賴其他重要的 Cookie,例如用於 記住我 功能的 REMEMBERME
Cookie,或用於雙重驗證的受信任裝置 Cookie,也應保留這些 Cookie。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
sub vcl_recv {
// Remove all cookies except for essential ones.
if (req.http.Cookie) {
set req.http.Cookie = ";" + req.http.Cookie;
set req.http.Cookie = regsuball(req.http.Cookie, "; +", ";");
set req.http.Cookie = regsuball(req.http.Cookie, ";(PHPSESSID|REMEMBERME)=", "; \1=");
set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");
if (req.http.Cookie == "") {
// If there are no more cookies, remove the header to get the page cached.
unset req.http.Cookie;
}
}
}
提示
如果內容並非每個使用者都不同,而是取決於使用者的角色,解決方案是按群組分隔快取。此模式由 FOSHttpCacheBundle 以 使用者情境 的名稱實作和說明。
確保一致的快取行為
Varnish 使用您的應用程式傳送的快取標頭來決定如何快取內容。但是,Varnish 4 之前的版本不遵守 Cache-Control: no-cache
、no-store
和 private
。為了確保行為一致,如果您仍在使用 Varnish 3,請使用以下組態
1 2 3 4 5 6 7 8 9 10
sub vcl_fetch {
// By default, Varnish3 ignores Cache-Control: no-cache and private
// https://www.varnish-cache.org/docs/3.0/tutorial/increasing_your_hitrate.html#cache-control
if (beresp.http.Cache-Control ~ "private" ||
beresp.http.Cache-Control ~ "no-cache" ||
beresp.http.Cache-Control ~ "no-store"
) {
return (hit_for_pass);
}
}
提示
您可以在 VCL 檔案的形式中看到 Varnish 的預設行為:Varnish 3 的 default.vcl,Varnish 4 的 builtin.vcl。
啟用邊緣端包含 (ESI)
如 邊緣端包含文章 中所述,Symfony 會偵測它是否與理解 ESI 的反向代理通訊。當您使用 Symfony 反向代理時,您無需執行任何操作。但是,為了讓 Varnish 而不是 Symfony 解析 ESI 標籤,您需要在 Varnish 中進行一些組態。Symfony 使用來自 Akamai 描述的 邊緣架構 的 Surrogate-Capability
標頭。
注意
Varnish 僅支援 ESI 標籤的 src
屬性(onerror
和 alt
屬性會被忽略)。
首先,設定 Varnish,使其透過將 Surrogate-Capability
標頭新增至轉發到後端應用程式的請求來宣告其 ESI 支援
1 2 3 4
sub vcl_recv {
// Add a Surrogate-Capability header to announce ESI support.
set req.http.Surrogate-Capability = "abc=ESI/1.0";
}
注意
標頭的 abc
部分並不重要,除非您有多個需要宣告其功能的「代理」。請參閱 代理功能標頭 以取得詳細資訊。
然後,最佳化 Varnish,使其僅在至少有一個 ESI 標籤時才解析回應內容,方法是檢查 Symfony 自動新增的 Surrogate-Control
標頭
1 2 3 4 5 6 7
sub vcl_backend_response {
// Check for ESI acknowledgement and remove Surrogate-Control header
if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
unset beresp.http.Surrogate-Control;
set beresp.do_esi = true;
}
}
提示
如果您遵循了關於確保一致快取行為的建議,則這些 VCL 函數已經存在。將程式碼附加到函數的末尾,它們不會互相干擾。
快取失效
如果您想要快取經常變更的內容,並且仍然向使用者提供最新版本,則需要使該內容失效。雖然 快取失效 允許您在內容過期之前從代理中清除內容,但這會增加快取設定的複雜性。
提示
開放原始碼 FOSHttpCacheBundle 透過協助您組織快取和失效設定,消除了快取失效的痛苦。
FOSHttpCacheBundle 的文件說明了如何為快取失效設定 Varnish 和其他反向代理。