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

Дата оновлення перекладу 2023-09-19

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

Так як кеш Symfony використовує стандартні HTTP кеш заголовки, може бути з легкістю замінено будь-яким іншим зворотнім проксі. Varnish - це потужний відкритий HTTP-акселератор, здатний швидко обслуговувати кешування змісту, включно з підтримкою Включень крайнніх сторін (ESI).

Змушуємо 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";
    }
}

Куки та кешування

За замовчуванням, більшість проксі, що кешують, не кешують нічого, якщо запит відправлено з куки або базовим заголовком аутентифікації . Це тому, що зміст сторінки має залежати від значення куки або заголовку аутентифікації.

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

Кукі, створені в JavaScript та використані лише у вхідному буфері (фронт-енді), наприклад, при використанні Google Analytics, тим не менше відправляються серверсу. Ці куки неважливі для вихідного буферу та не мають впливати на рішення кешування. Сконфігуруйте ваш кеш Varnish, щоб він очищував заголовок куки. Вам варто залишити куки сесії, якщо вони є, та позбавитися всіх інших, щоб сторінки кешувалися, якщо активної сесії немає. Якщо ви не змінювали конфігурацію PHP за замовчуванням, то ваш куки сесії має ім'я PHPSESSID:

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:

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.

Активація включень крайніх сторін (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 додає автоматично:

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 та інші зворотні проксі для інвалідації кешування.