如何使用 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 路由器在使用 Varnish 时正确生成 URL,必须存在 X-Forwarded-Port
标头,以便 Symfony 使用正确的端口号。
此端口号对应于您的设置用于接收外部连接的端口(80
是 HTTP 连接的默认值)。如果应用程序也接受 HTTPS 连接,则可能在默认 HTTPS 端口 443 上有另一个代理(因为 Varnish 本身不做 HTTPS),该代理处理 SSL 终止并将请求作为 HTTP 请求转发到 Varnish,并带有 X-Forwarded-Proto
标头。在这种情况下,您需要添加以下配置代码段
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 以在负载均衡器或反向代理后工作。
Cookies 和缓存
默认情况下,当使用 cookies 或基本身份验证标头发送请求时,大多数缓存代理不会缓存任何内容。这是因为页面的内容应该取决于 cookie 值或身份验证标头。
如果您确定后端从不使用会话或基本身份验证,请让 Varnish 从请求中删除相应的标头,以防止客户端绕过缓存。在实践中,您至少需要站点某些部分的会话,例如,当使用带有 CSRF 保护的表单时。在这种情况下,请确保 仅在实际需要时才启动会话,并在不再需要会话时清除会话。或者,您可以研究 缓存包含 CSRF 保护表单的页面。
在 JavaScript 中创建且仅在前端使用的 Cookie(例如来自 Google Analytics 的 Cookie)仍然会发送到服务器。这些 Cookie 与后端处理无关,不应影响缓存逻辑。为了确保这一点,请配置您的 Varnish 缓存以 清理 cookies 标头,仅保留必要的 cookie(例如,会话 cookie)并删除所有其他 cookie。这样可以使页面在没有活动会话时被缓存。
如果您使用 PHP 及其默认配置,则会话 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
部分并不重要,除非您有多个需要声明其功能的“代理”。有关详细信息,请参阅 Surrogate-Capability 标头。
然后,优化 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 和其他反向代理。