Содержание статьи
Сразу переключитесь на отдачу предгенерированных страниц прямо из файловой системы, минуя PHP и MySQL. Это снижает нагрузку почти до нуля. Nginx умеет это делать с минимальной конфигурацией, главное – соблюсти структуру и порядок условий.
Самый прямой путь – использовать директиву try_files
для приоритета файлов перед проксированием в backend. Рабочий шаблон для index:
location / {
try_files /cache/$uri/index.html /cache/$uri.html /index.php?$args;
}
Точка монтирования: каталог /cache
должен содержать актуальные HTML-слепки страниц. Их нужно обновлять при публикации, обновлении или удалении контента. Поддерживать это можно скриптами, хуками или сторонними плагинами, пишущими в файловую систему.
Обходной путь для страниц с динамическими элементами – разбивать шаблон на фрагменты, отдавая сквозные блоки через Edge Side Includes или JavaScript. Полный захват кеша допустим только при отсутствии авторизации, куки или специфических параметров запроса.
Внимание! Не забывайте проверять наличие куки
wordpress_logged_in_*
– если она установлена, нужно обойти кеш. Иначе залогиненные пользователи получат чужие страницы.
Базовая проверка в конфиге:
set $skip_cache 0;
if ($http_cookie ~* \"wordpress_logged_in_\") {
set $skip_cache 1;
}
Условие отдачи статического файла:
location ~* \\.html$ {
if ($skip_cache = 0) {
expires 30m;
add_header X-Cache-Status \"HIT\";
}
if ($skip_cache = 1) {
add_header X-Cache-Status \"BYPASS\";
return 444;
}
}
Подмена ответов 444 через fallback в PHP обеспечивается отдельно. Такой подход убирает лишние проверки из основного location и делает отладку прозрачной.
Важно помнить: каждый лишний if в конфигурации Nginx влияет на производительность. Логика должна быть плоской и предсказуемой.
Сама генерация HTML-копий может выполняться плагинами типа WP Super Cache, но куда надежнее – собственным кодом на хуках save_post
и delete_post
. Это позволяет контролировать структуру путей, исключать нежелательные страницы и учитывать переменные шаблонов.
Зачем это всё? Потому что PHP медленный, а MySQL – не подарок. Вам не нужен рендер для страницы, которую можно просто прочитать с диска.
Включите логирование пропущенных запросов и анализируйте логи – там правда. Только по ним видно, что именно обходится, а что действительно работает на пределе.
Настройка fastcgi_cache для кэширования страниц WordPress
Создайте директорию под хранилище:
mkdir -p /var/cache/nginx/fastcgi_temp
Назначьте владельца и права, иначе получите 403:
chown -R www-data:www-data /var/cache/nginx
chmod -R 755 /var/cache/nginx
В блок http добавьте:
fastcgi_cache_path /var/cache/nginx/fastcgi_temp levels=1:2 keys_zone=WPZONE:100m inactive=60m use_temp_path=off;
fastcgi_cache_key \"$scheme$request_method$host$request_uri\";
Без уникального ключа кэшировать не будет ничего. Или будет не то.
Теперь в server-блок:
set $skip_cache 0;
if ($request_method = POST) {
set $skip_cache 1;
}
if ($query_string != \"\") {
set $skip_cache 1;
}
if ($http_cookie ~* \"comment_author_|wordpress_[a-f0-9]+|wp-postpass_|wordpress_logged_in\") {
set $skip_cache 1;
}
location ~ .php$ {
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_cache WPZONE;
fastcgi_cache_valid 200 301 302 10m;
add_header X-FastCGI-Cache $upstream_cache_status;
}
Проверяйте заголовок X-FastCGI-Cache
. HIT или MISS – разница принципиальна.
Проблема с формами? Да. POST-запросы не кэшируются. И это хорошо. Но если что-то работает неправильно – ищите в $skip_cache
.
Внимание! После каждого изменения в конфигурации не забывайте:
nginx -t && systemctl reload nginx
. Без этого ничего не заработает.
Удаление вручную? Без менеджера кэша – только так:
rm -rf /var/cache/nginx/fastcgi_temp/*
Или пишите крон:
*/30 * * * * root find /var/cache/nginx/fastcgi_temp -type f -mmin +60 -delete
Для wp-admin и wp-login.php кэш противопоказан. Укажите:
location ~* ^/(wp-admin|wp-login\\.php) {
fastcgi_cache_bypass 1;
fastcgi_no_cache 1;
}
Важно помнить: любая ошибка в регулярных выражениях может сделать сайт недоступным или бесконечно кэшируемым. Проверяйте каждый символ.
И последнее. Не полагайтесь на плагины. Nginx работает до PHP. Ошибетесь – и будете кэшировать админку, сессии и logout.
Нужен контроль? Используйте curl -I https://example.com
. Анализируйте заголовки. Каждый байт важен.
Исключение авторизованных пользователей из кэширования
Отключайте буферизацию для вошедших в систему. Безоговорочно. Это прямое требование. Сохранение страниц с админ-панелью, нотификациями или формами отправки может сломать интерфейс и вызвать утечку данных. Используйте проверку на наличие cookie авторизации.
Пример в конфигурации:
if ($http_cookie ~* \"wordpress_logged_in_\") {
set $skip_cache 1;
}
Далее – учитывайте методы HTTP. POST, PUT, DELETE всегда обходят кэш. Не забывайте исключать такие запросы в логике работы. Пример:
if ($request_method != GET) {
set $skip_cache 1;
}
Помимо cookie wordpress_logged_in_
, необходимо также исключать wp-admin
и wp-login.php
по URI.
if ($request_uri ~* \"/wp-admin/|/wp-login.php\") {
set $skip_cache 1;
}
Сложность: не только прямой вход пользователя, но и фоновые AJAX-запросы. Они часто отправляются с куками авторизации. Не поймаете – получите неконсистентную выдачу.
Добавьте логгирование условий отключения кеша. Это спасет от диагностики вслепую:
access_log /var/log/nginx/no-cache.log combined if=$skip_cache;
Важно! Если пользователь видит не свою админку – ошибка не в шаблоне, а в механизме буферизации. Проверяйте cookie в первую очередь.
Иногда разработчики допускают роковую ошибку: отключают буфер только для wp-admin
, но не для API-запросов, приходящих с фронта. Итог – редактирование чужих данных.
Помните: авторизация – главный маркер для исключения страницы из повторной выдачи. Всё остальное – вторично.
Проблему нельзя решать через expires
или cache-control
. Только через условия в конфигурации. Причина – куки не видны браузеру, а только бэкенду. Не обманете.
Резюме: логика исключения должна быть жёсткой, не подверженной компромиссам. Нет исключений – будут баги.
Обработка сброса кэша при публикации и обновлении контента
Настраивайте хуки save_post
, edit_post
и transition_post_status
для немедленного удаления закэшированных файлов после сохранения записи. Иначе пользователь увидит устаревшую страницу даже после публикации нового материала.
Пример функции, удаляющей HTML-файл при изменении контента:
function purge_static_cache($post_id) {
if (wp_is_post_revision($post_id)) return;
$url = get_permalink($post_id);
$path = str_replace(home_url(\'/\'), \'\', $url);
$cache_file = \'/var/www/cache/\' . rtrim($path, \'/\') . \'/index.html\';
if (file_exists($cache_file)) unlink($cache_file);
}
add_action(\'save_post\', \'purge_static_cache\');
add_action(\'edit_post\', \'purge_static_cache\');
add_action(\'deleted_post\', \'purge_static_cache\');
С этим всё просто. Но где настоящие ловушки? В custom post types, в REST-запросах, в пагинации, в таксономиях. Вы удалили одну страницу, но забыли про архив? Про страницы пагинации? Тогда пользователь смотрит в пустоту и не понимает, почему новый пост не появился.
Важно помнить: нельзя очищать только текущую запись – сбрасывайте связанные страницы и разделы!
Сброс по цепочке. Меняется запись – удаляется главная, архив, таксономии. Если используете плагин типа WP Super Cache, интегрируйте удаление с его API. Пример для него:
if (function_exists(\'prune_super_cache\')) {
prune_super_cache(get_home_path() . \'wp-content/cache/supercache/\', true);
}
Если используется fastcgi_cache, управляйте через отправку запроса на сервер. Пример:
function purge_fastcgi($url) {
$host = parse_url($url, PHP_URL_HOST);
$path = parse_url($url, PHP_URL_PATH);
$fp = fsockopen(\'127.0.0.1\', 80, $errno, $errstr, 1);
if ($fp) {
$out = \"PURGE $path HTTP/1.0\\r\\n\";
$out .= \"Host: $host\\r\\n\\r\\n\";
fwrite($fp, $out);
fclose($fp);
}
}
Так удаляется нужная версия с сервера. Быстро. Без лишнего. Но не забудьте: если используется HTTPS, это не сработает через порт 80. Настройте Nginx на приём метода PURGE или используйте сторонний API.
Внимание! Использование метода PURGE должно быть ограничено IP-адресами или авторизацией, иначе любой сможет стереть содержимое сервера.
Автоматизируйте: wp_cron – слабое звено, если трафик низкий. Замените системным cron, иначе сброс может не произойти вовремя. Скрипт можно вызывать через CLI, curl или по webhook.
Не забывайте про JSON endpoints – фронтенд на Vue или React может получать устаревшие данные, если вы не сбрасываете связанные endpoints. Решение – метки версий или инвалидирование через REST-хуки.
Надоело вручную? Используйте плагин с поддержкой CLI-команд. Пример:
wp cache flush
Это вычищает всё. Беспощадно. Без диалогов. Осторожнее в проде.
Проверка работы кэша и устранение типичных проблем
Проверь заголовки ответа сервера. Если отсутствует X-Cache: HIT
или его аналог, значит отдаётся не сохранённая копия, а живая страница. Это провал. Используй curl -I https://example.com/
или расширение для браузера, фиксирующее HTTP-заголовки.
Причина №1 – авторизованные пользователи. Для них в большинстве конфигураций обрабатываются динамические страницы. Открой сайт в приватной вкладке. Или ещё проще: добавь в конфигурацию исключение для Cookie Set-Cookie
и Cookie
.
location / {
try_files /cache$uri/index.html $uri $uri/ /index.php?$args;
add_header X-Cache $upstream_cache_status;
proxy_cache_bypass $http_cookie;
}
Вторая по частоте проблема – наличие GET-параметров. Особенно таких, как ?utm_source=
, ?fbclid=
, ?gclid=
. Они мешают отдаче уже сохранённого HTML. Удаляй эти параметры на уровне конфигурации или через промежуточный прокси.
Внимание! Даже один непредусмотренный параметр в URL создаёт новую копию страницы и сбрасывает сохранённый результат.
Следующий шаг – проверка наличия/отсутствия файлов. Папка, где хранятся отрендеренные версии страниц, должна быть доступна для чтения. Проверяй права: chmod 755
и chown nginx:nginx
на директории.
Обрати внимание на элементы, принудительно отключающие сохранение: заголовки Cache-Control: no-store
, Pragma: no-cache
, Set-Cookie
. Их нужно вырезать через proxy_hide_header
или пересобрать через fastcgi_cache
с фильтрами.
proxy_hide_header Set-Cookie;
proxy_hide_header Cache-Control;
Неправильные ключи кеширования – ещё одна мина. Например, если ключ зависит от $scheme
, то http и https будут считаться разными страницами. Сократи ключ до минимально необходимого:
fastcgi_cache_key \"$host$request_uri\";
Важно помнить: плагин для управления сохранёнными версиями страниц может конфликтовать с серверными правилами. Особенно WP Super Cache и его режимы. Один работает через мод_rewrite, другой – через PHP. Выбери только один метод.
Помните! Включённый режим debug в плагине может отключить сохранение на уровне сервера.
Проверь наличие robots.txt
и .htaccess
(если используется Apache рядом). Иногда они ограничивают доступ к файлам, нужным для сохранённой версии – как CSS или JS. Невидимая ошибка, которая убивает производительность.
И наконец: попробуй отключить весь код в functions.php
, временно переименовав файл. Некоторые фильтры WordPress вызывают динамическую генерацию даже при правильной конфигурации сервера.
Проверка завершена? Ответ должен содержать 200 OK
и X-Cache: HIT
, а скорость – в миллисекундах. Всё остальное – мусор и ошибки, которые нужно искоренять.