Как использовать Varnish для ускорения моего сайта

Дата обновления перевода 2023-01-16

Как использовать Varnish для ускорения моего сайта

Так как кеш Symfony использует стандартные HTTP кеш заголовки, может быть с лёгкостью заменён любым другим обратным прокси. Varnish - это мощный открытый HTTP-акселератор, способный быстро обслуживать кешированное содержание, включая поддержку Включений крайней стороны.

Заставляем Symfony доверять обратному прокси

Varnish автоматически пересылает IP как X-Forwarded-For и оставляет загловок X-Forwarded-Proto в запросе. Если вы не сконфигурируете Varnish как доверенный прокси, то Symfony будет видеть все запросы, как исходящие от незащищённых HTTP соединений хоста Varnish, а не от реального клиента.

Не забудьте вызвать метод Request::setTrustedProxies() в вашем фронт-контроллере, чтобы Varnish выглядел как доверенный прокси и были использованы заголовки X-Forwarded-* .

Маршрутизация и заголовки X-FORWARDED

Чтобы убедиться в том, что маршрутизатор Symfony правильно генерирует URL с Varnish, должен присутствовать заголовок X-Forwarded-Port, чтобы Symfony использовала правильный номер порта.

Этот номер порта соответствует тому, что использует ваша установка для извлечения внешних связей (80 - значение по умолчанию для HTTP-соединений). Если приложение также принимает HTTP-соединения, то может быть ещё один прокси (так как Varnish сам не делает HTTPS) в порту HTTPS по умолчанию 443, который обрабатывает завершение 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";
    }
}

Куки и кеширование

По умолчанию, большинство кеширующих прокси, не кешируют ничего, если запрос отправлен с куки или базовым заголовком аутентификации . Это потому, что содержимое страницы должно зависеть от значения cookie или заголовка аутентификации.

Если вы точно знаете, что выходной буфер никогда не использовает сессии или базовую аутентификацию, сделайте так, чтобы Varnish удалил соответствующий загловок из запросов, чтобы предотвратить клиентов от обхода кеширования. На практике, вам понадобятся сессии как минимум для некоторых частей сайта, например, при использовании форм с CSRF-защитой. В этой ситуации, убедитесь, что вы начинаете сессию только тогда, когда это действительно нужно и очищаете сессию, когда она уже не нужна. Как вариант, вы можете рассмотреть Кеширование страниц, содержащих CSRF-защищённые формы.

Cookie, созданные в JavaScript и использованные только в входном буфере (фронт-енде), например, при использовании Google Analytics, тем не менее отправляются серверу. Эти cookie неважны для выходного буфера и не должны влиять на решение кешироваия. Сконфигурируйте ваш кеш Varnish, чтобы он очищал заголовок cookie. Вам стоит оставить cookie сессии, если он есть, и избавиться от всех остальных, чтобы страницы кешировались, если активной сессии нет. Если вы изменяли конфигурацию PHP по умолчанию, то ваш cookie сессии имеет имя PHPSESSID:

  • Varnish 4
  • Varnish 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sub vcl_recv {
    // Удалить все куки, кроме ID сессии.
    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)=", "; \1=");
        set req.http.Cookie = regsuball(req.http.Cookie, ";[^ ][^;]*", "");
        set req.http.Cookie = regsuball(req.http.Cookie, "^[; ]+|[; ]+$", "");

        if (req.http.Cookie == "") {
            // Если куки больше нет, удалите заголовок для кеширования страницы.
            unset req.http.Cookie;
        }
    }
}

Tip

Если содержимое не отличается для каждого пользователя, но зависит от ролей пользователя, то решением будет разделять кеширование по группам. Эта схема реализуется и объясняется FOSHttpCacheBundle под названием Контекст пользователя.

Гарантия последовательного поведения кеширования

Varnish использует заголовки кеша, отправленные вашим приложением, чтобы определить, как кешировать содержимое. Однако, версии Varnish до 4 не уважали Cache-Control: no-cache, no-store и private. Чтобы гарантировать последовательное поведение, используйте следуюзую конфигурацию, если вы всё ещё используете Varnish 3:

  • Varnish 3
1
2
3
4
5
6
7
8
9
10
sub vcl_fetch {
    /* По умолчанию, Varnish3 игнорирует Cache-Control: no-cache и 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);
    }
}

Tip

Вы можете увидеть поведение Varnish по умолчанию в форме VCL-файла: default.vcl для Varnish 3, builtin.vcl для Varnish 4.

Активация включений крайней стороны (Edge Side Includes (ESI))

Как объясняется в статье об ESI, Symfony определяет, общается она с обратным прокси, понимающим ESI, или нет. Когда вы используете обратный прокси Symfony, вам не нужно ничего делать. Но чтобы заставить Varnish разрешать ESI-теги вместо Symfony, вам нужна некоторая конфигурация в Varnish. Symfony использует заголовок Surrogate-Capability из Edge Architecture, описанный Akamai.

Note

Varnish поддерживает только атрибут src для ESI-тегов (атрибуты onerror и alt игнорируются).

Для начала, сконфигурируйте Varnish так, чтобы он афишировал свою поддержку ESI путём добавления заголовка Surrogate-Capability в запросы, пересылаемые приложению выходного буфера:

1
2
3
4
sub vcl_recv {
    // Добавить заголовок Surrogate-Capability, чтобы афишировать поддержку ESI.
    set req.http.Surrogate-Capability = "abc=ESI/1.0";
}

Note

Часть заголовка abc неважна, кроме случаев, если у вас множество "сурогатов", которые должны афишировать свои возможности. Смотрите Заголовок Surrogate-Capability, чтобы узнать детали.

Далее, оптимизируйте Varnish так, чтобы он анализировал содержимое ответа только тогда, когда в нём есть хотя бы один ESI-тег, проверяя заголовок Surrogate-Control, который Symfony добавляет автоматически:

  • Varnish 4
  • Varnish 3
1
2
3
4
5
6
7
sub vcl_backend_response {
    // Проверить подтверждение ESI и удалить заголовок Surrogate-Control
    if (beresp.http.Surrogate-Control ~ "ESI/1.0") {
        unset beresp.http.Surrogate-Control;
        set beresp.do_esi = true;
    }
}

Tip

Если вы последовали совету о гарантировании последовательного поведеия кеширования, то эти VCL-функции уже существуют. Просто добавьте код к концу функции, они не будут мешать друг другу.

Инвалидация кеша

Если вы хотите кешировать содержимое, которое часто меняется, и всё равно отправлять пользователям наиболее свежую версию, то вам нужно инвалидировать это содержимое. В то время, как инвалидация кеша позволяет вам извлекать содержимое из вашего прокси до того, как истечёт срок действия, она добавляет сложности вашей установке кеширования.

Tip

FOSHttpCacheBundle открытого доступа избавляет от головной боли с инвалидацией, помогая вам организовать вашу установку кеширования и инвалидации.

Документация FOSHttpCacheBundle разъясняет, как сконфигурировать Varnish и другие обратные прокси для инвалидации кеширования.