跳到内容

如何使用 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-cacheno-storeprivate。为了确保行为一致,如果您仍在使用 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 属性(onerroralt 属性被忽略)。

首先,配置 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 和其他反向代理。

这项工作,包括代码示例,根据 Creative Commons BY-SA 3.0 许可获得许可。
目录
    版本