Когда трафик на WordPress перестаёт влезать в один сервер, логичный шаг — унести сайт на несколько VDS и повесить спереди балансировщик (чаще всего Nginx или HAProxy). В теории всё просто: несколько PHP‑нод, общий сервер базы данных, и балансировщик равномерно раздаёт нагрузку. На практике же WordPress любит писать файлы на диск и хранить состояние в сессиях, из‑за чего кластер внезапно начинает вести себя непредсказуемо.
В этой статье разберём практическую схему для WordPress на нескольких VDS:
- как организовать sticky sessions на уровне балансировщика;
- как сделать shared uploads (общую директорию
wp-content/uploads) для всех нод; - как обращаться с сессиями, кэшем и очередями заданий;
- какие типовые грабли вас ждут и как их обойти.
Рассматриваем классический стек: Nginx в роли балансировщика, несколько бэкендов с Nginx + PHP‑FPM (или только PHP‑FPM, если балансировщик напрямую ходит в FastCGI), отдельный MySQL/MariaDB.
Базовая архитектура WordPress-кластера на VDS
Начнём с целевой схемы, к которой мы будем подводить конфиги и практику. Условно:
- VDS #1 — балансировщик (Nginx): принимает HTTPS‑трафик, терминирует TLS, при желании раздаёт часть статики и проксирует PHP‑запросы на пул бэкендов.
- VDS #2, #3 — веб‑ноды (Nginx + PHP‑FPM или только PHP‑FPM, если Nginx один на фронте).
- VDS #4 — база данных (MySQL/MariaDB, может быть управляемый сервис или отдельный кластер, но в статье концентрируемся именно на веб‑части).
- Хранилище для файлов: NFS/Samba, объектное хранилище S3‑совместимое или примонтированный том к нескольким нодам (в зависимости от платформы).
Ключевые проблемы, которые нужно решить:
- Сессии пользователей: чтобы авторизация в админке и корзина в WooCommerce не «сбрасывались» при переходе на другой бэкенд.
- Загрузки файлов: чтобы картинка, загруженная на ноду #2, тут же была доступна, если следующий запрос попал на ноду #3.
- Кэш и фоновая работа: чтобы wp‑cron, object cache и page cache не ломали консистентность.
Sticky‑сессии и shared storage как раз и решают эти задачи.
Что такое sticky sessions и зачем они WordPress
Sticky sessions (session persistence) — это режим балансировщика, при котором запросы одного и того же клиента стабильно отправляются на один и тот же бэкенд. Идея простая: пока у клиента живёт cookie или TCP‑сеанс, сервер, обслуживающий его, не меняется.
Для WordPress это актуально по нескольким причинам:
- PHP‑сессии. Если вы используете файловые сессии (
session.save_handler = files) на локальном диске, а балансировщик кидает пользователя то на один, то на другой сервер, то сессии «теряются». Sticky‑режим частично решает проблему: пока живёт cookie, клиент ходит в одну ноду. - Локальные кеши. Некоторый плагин может использовать локальный файловый или in‑memory кеш. Без sticky‑сессий часть запросов увидит кеш, а часть — нет.
- Долгие операции в админке. Импорт, миграции, операции с плагинами. Если запросы могут «прыгать» между нодами, поведение становится нестабильным.
Однако sticky‑сессии — это костыль, а не финальное решение. Идеальный вариант для WordPress‑кластера:
- PHP‑сессии хранятся в централизованном хранилище (Redis, Memcached, база данных).
- Объектный кэш тоже централизован (Redis/Memcached).
- Sticky нужен только как дополнительный слой надёжности, а не как единственная опора.
Тем не менее в реальном мире часто начинают именно со sticky‑сессий, а уже потом переносят сессии и кэш в Redis/Memcached. Поэтому мы рассмотрим оба подхода.
Sticky sessions в Nginx: базовая схема
Один из простейших способов реализовать минимальную «прилипчивость» в Nginx без сторонних модулей — использовать режим ip_hash или привязку к cookie через hash в upstream, если у вас сборка Nginx с модулем sticky (например, коммерческий или от дистрибутива с патчами).
Типичная конфигурация с ip_hash выглядит так:
upstream php_backends {
ip_hash;
server 10.0.0.2:9000 max_fails=3 fail_timeout=30s;
server 10.0.0.3:9000 max_fails=3 fail_timeout=30s;
}
server {
listen 443 ssl;
server_name example.com;
location ~ \.php$ {
fastcgi_pass php_backends;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
ip_hash делает привязку клиента к серверу по IP‑адресу. Это лучше, чем ничего, но есть минусы:
- пользователи за NAT (мобильные сети, офисы) окажутся все на одной ноде;
- при смене IP (роуминг, мобильный интернет) «прилипчивость» пропадает;
- нет тонкой настройки TTL для сессии.
Более современный вариант — sticky по cookie. Для этого обычно используют либо коммерческий Nginx Plus, либо сторонние модули (например, nginx-sticky-module-ng), либо переносят sticky‑логику в HAProxy. Архитектурно это выглядит так:
upstream php_backends {
sticky cookie srv_id expires=1h domain=.example.com path=/;
server 10.0.0.2:9000;
server 10.0.0.3:9000;
}
Балансировщик устанавливает cookie srv_id, и пока оно валидно, запросы летят на одну и ту же ноду. Смысл в том, что WordPress (и браузер пользователя) не знают о sticky‑механике — всё прозрачно и делается только на уровне балансировщика.
Shared uploads: общая директория для файлов
Вторая большая проблема WordPress в кластере — файлы. Любой аплоад через медиа‑библиотеку, плагин или тему попадает в wp-content/uploads (по умолчанию) или в другую директорию, если вы её переопределяли.
Если у вас несколько веб‑нод и каждая пишет файлы на локальный диск, получится ситуация:
- пользователь загрузил картинку на ноду #2, она легла в
/var/www/site/wp-content/uploads/2025/11/на ноде #2; - следующий HTTP‑запрос на эту картинку попал на ноду #3 — и там файла нет;
- в результате 404, битые картинки, недогруженный контент.
Решить это можно тремя основными способами:
- общая файловая директория (NFS, GlusterFS, примонтированный shared‑том и т. п.);
- S3‑совместимое хранилище и offload файлов через плагин для WordPress;
- синхронизация файлов между нодами (rsync, lsyncd), но это худший вариант для активно меняющегося контента.
Вариант 1: NFS/сетевой том для wp-content/uploads
Самый понятный для классического админа подход — отдельный сервер или managed‑хранилище с NFS, где хранится общая папка:
- на ноде‑хранилище экспортируется каталог, например
/srv/wordpress-uploads; - на всех веб‑нодах этот каталог монтируется как
/var/www/site/wp-content/uploads; - все ноды видят один и тот же набор файлов.
С точки зрения WordPress ничего не поменялось — он по‑прежнему считает, что пишет на локальный диск. Просто теперь этот диск физически общий.
Ключевые моменты по NFS для WordPress:
- обязательно следите за правами и владельцами файлов (uid/gid должны совпадать на всех нодах и NFS‑сервере);
- настройте простой мониторинг доступности NFS (иначе в один прекрасный день WordPress вдруг начнёт падать с ошибками прав или записи);
- проверьте, что NFS не становится «бутылочным горлышком» по IOPS/латентности (медиа‑сайт с тысячами мелких файлов может нагрузить хранилище сильно).
Монтаж каталога на веб‑ноде обычно делается через /etc/fstab или unit systemd, важно обеспечить порядок запуска: сеть → NFS → PHP‑FPM/Nginx.
Вариант 2: S3-хранилище и offload файлов
Более современный подход — не делиться файловой системой, а отдать статические файлы в объектное хранилище (S3‑совместимое). WordPress‑плагины умеют прозрачно загружать всё в бакет и подменять URL картинок и медиа‑файлов на адрес CDN или самого бакета.
Плюсы такого подхода:
- хранилище легко масштабируется по объёму;
- проще строить CDN и offload трафика;
- веб‑ноды перестают страдать от лишнего I/O по диску.
Минусы:
- нужно правильно настроить плагины, права, политики доступа и кеширование;
- могут быть нюансы с плагинами, которые ожидают локальное присутствие файлов;
- отладка сложнее, особенно если возникают проблемы с ACL и политиками.
Для простоты старта многие админы сначала настраивают NFS или общий том, а уже потом переносят статический контент на S3.
Отдельно имеет смысл продумать, как вы будете обновлять сам WordPress и плагины. В многонодовых конфигурациях удобно держать код в Git и деплоить его на все ноды единообразно, а хранилище uploads держать общим через NFS или объектное хранилище.

Сессии, кэш и sticky: как всё состыковать
Допустим, у нас есть:
- Nginx‑балансировщик со sticky‑сессиями.
- Несколько веб‑нод с общим
wp-content/uploadsчерез NFS или S3. - Отдельный сервер Redis (или Memcached) для кэша и сессий.
Теперь главный вопрос: как правильно раскидать данные, чтобы WordPress вёл себя предсказуемо и не ломался?
PHP-сессии в Redis/Memcached вместо файлов
Если вы оставите стандартные файловые сессии (session.save_handler = files) на локальном диске, вы будете очень зависеть от sticky‑сессий и не сможете гибко масштабировать ноды. Любое изменение конфигурации балансировщика или ротация нод будет приводить к потерям сессий.
Правильный путь — хранить сессии в централизованном хранилище:
- Redis (через расширение
redisдля PHP); - Memcached.
Тогда любая нода может обслуживать запрос пользователя и видеть его сессию. Sticky‑сессии превращаются в приятный бонус (уменьшают число случайных «перепрыгиваний» во время долгоживущих операций), но не жизненно необходимы.
Пример минимальной настройки для Redis в php.ini (или в отдельном файле в conf.d):
session.save_handler = redis
session.save_path = "tcp://10.0.0.10:6379?database=2&prefix=wp_sess:"
Важно:
- выделите отдельную базу или префикс под сессии;
- поставьте разумный
session.gc_maxlifetime, чтобы не засорять Redis старыми ключами; - учтите, что одна и та же сессия может обратиться к разным нодам — не храните в локальных файлах состояние, связанное с сессией.
Про плюсы и минусы Redis и Memcached для PHP и WordPress подробнее можно почитать в статье о выборе кэширующего бэкенда Redis или Memcached для WordPress и PHP.
Object cache и page cache
Следующий уровень — object cache (WP_Object_Cache). Здесь тоже имеет смысл использовать Redis или Memcached, причём общий для всех нод. Так плагины кэширования (и само ядро WordPress) получат единое хранилище данных между запросами.
Основные правила:
- используйте проверенный плагин object cache (например, для Redis);
- убедитесь, что все ноды используют один и тот же Redis и одинаковые настройки префикса;
- следите за размером кэша и eviction‑политиками, иначе можно получить неожиданные сбросы.
С page cache есть два пути:
- Кэшировать страницы на уровне WordPress (плагины) с записью в Redis или файлы.
- Кэшировать на уровне Nginx (fastcgi_cache, proxy_cache) или внешнего прокси (Varnish, CDN).
В кластерной конфигурации чаще всего логичнее вынести page cache на уровень Nginx или внешнего прокси, чтобы разгрузить PHP и получить детерминированное поведение. Тогда все веб‑ноды становятся скорее PHP‑«воркерами», а кэш живёт на фронтовом уровне.
Настройка Nginx: sticky + shared storage на практике
Теперь соберём пример конфигурации Nginx‑балансировщика, который:
- проксирует запросы на пул фронтендов с WordPress;
- использует sticky‑сессии для HTTP‑трафика;
- корректно передаёт IP‑адреса и схему.
Рассмотрим вариант, когда фронтовый Nginx не содержит сам WordPress‑файлы, а просто проксирует HTTP на ноды с Nginx+PHP‑FPM (так проще гибко масштабировать):
upstream wp_frontends {
# Здесь может быть либо ip_hash, либо sticky cookie в зависимости от модуля
ip_hash;
server 10.0.0.2:80 max_fails=3 fail_timeout=30s;
server 10.0.0.3:80 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
server_name example.com;
# HTTP -> HTTPS редирект опустим для краткости
location / {
proxy_pass http://wp_frontends;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
На веб‑нодах Nginx уже работает как обычный фронтенд для WordPress, при этом wp-content/uploads смонтирован из shared‑хранилища. В таком случае веб‑нодам не нужны дополнительные sticky‑механизмы: всё решается на верхнем уровне.
Если вы строите схему «Nginx‑балансировщик → PHP‑FPM pool» (без Nginx на бэкендах), то статика может раздаваться с фронтового Nginx, а fastcgi_pass делать на несколько PHP‑FPM‑нод с sticky (cookie или ip_hash). Тогда shared‑storage нужно подключать уже к фронтовому Nginx, а не к каждой ноде.
Для тонкой настройки кеша на уровне Nginx (включая работу с картинками и диапазонными запросами) могут быть полезны материалы про кэширование статики и современных форматов изображений в Nginx.

Типичные грабли и как их избежать
При переходе с одиночного сервера WordPress на кластер из нескольких VDS вы с высокой вероятностью столкнётесь с такими проблемами.
1. Нестабильная авторизация в админке и WooCommerce
Симптомы: залогинился — всё ок, перешёл по другой ссылке — внезапно разлогинило, корзина «слетает», иногда выкидывает 403 или 401.
Чаще всего виноваты либо PHP‑сессии на локальном диске, либо кэш заголовков и кук на фронтовом уровне. Проверки:
- убедитесь, что сессии вынесены в Redis или Memcached;
- проверьте, что балансировщик не кэширует
Set-Cookieи не выкидывает cookie; - если используете page cache на фронте, исключите из него страницы логина, корзины, чекаута, личного кабинета.
2. Битые картинки и файлы после переноса на кластер
Симптомы: часть картинок отдаётся как 404, некоторые файлы есть на одной ноде и отсутствуют на другой.
Причина очевидна — нет корректного shared‑storage. Временное решение (до внедрения NFS или S3) — синхронизировать wp-content/uploads между нодами через rsync, но для активных сайтов это быстро становится неудобным и небезопасным (рассинхронизация почти гарантирована).
Лучше сразу внедрить:
- NFS или сетевой том с общими правами;
- или S3‑offload через плагин.
3. Дублирование фоновых задач и кронов
В одиночной инсталляции WordPress многие задачи выполняются через wp‑cron (HTTP‑запросы к wp-cron.php). В кластере, если кросс‑кеш и sticky настроены неправильно, можно получить ситуацию, когда:
- wp‑cron срабатывает сразу на нескольких нодах;
- импорты, рассылки или очереди обрабатываются в дублирующемся режиме;
- часть задач не выполняется, потому что любая нода считает, что это уже сделал «кто‑то другой».
Рекомендуемая стратегия:
- Отключить встроенный wp‑cron (константа
DISABLE_WP_CRONвwp-config.php). - Настроить системный cron на одну чётко определённую ноду, которая будет раз в минуту (или чаще) вызывать
wp-cron.phpчерез CLI или HTTP. - Если используете очередь задач (например, через сторонние плагины), убедитесь, что воркеры привязаны либо к одному хосту, либо используют централизованную очередь (RabbitMQ, Redis).
4. Разъезд конфигов и версий кода между нодами
Ещё один частый случай: админ обновляет плагин или тему вручную на одной ноде (через SFTP или встроенный файловый редактор), а вторая нода остаётся на старой версии. В результате:
- часть запросов обслуживается старым кодом, часть — новым;
- возникают «невоспроизводимые» баги, которые ловятся только на конкретной ноде.
Правильный путь для кластера:
- хранить код WordPress и плагинов в системе контроля версий (Git);
- разворачивать одно и то же состояние кода на все ноды через CI/CD или хотя бы через
rsyncиз единого источника; - запретить прямые изменения кода из админки (константа
DISALLOW_FILE_EDITи, по возможности,DISALLOW_FILE_MODS).
Иными словами, в многонодовой конфигурации админке не стоит доверять обновления ядра и плагинов напрямую на боевом окружении. Лучше использовать staging и аккуратный деплой.
Пошаговый план миграции WordPress на кластер VDS
Соберём всё в единый чек‑лист, который можно использовать как ориентир при планировании:
- Подготовить VDS‑инфраструктуру. Разделить роли: фронтовый Nginx/балансировщик, 2+ веб‑ноды, база данных, опционально отдельный Redis и файловое хранилище.
- Вынести базу данных. Перенести MySQL/MariaDB на отдельный сервер (если ещё не вынесена), настроить резервное копирование и мониторинг.
- Организовать shared uploads. Выбрать NFS или S3, поднять и протестировать общую директорию для
wp-content/uploads. Убедиться, что все ноды видят одни и те же файлы. - Настроить Redis/Memcached для сессий и object cache. Подключить их к PHP и WordPress, проверить корректность работы авторизации и кэша.
- Включить sticky‑сессии на балансировщике. Выбрать и настроить механизм (IP‑hash или cookie‑based), протестировать на тестовом домене.
- Вынести wp‑cron в системный cron. Отключить встроенный wp‑cron, назначить одну ноду ответственной за запуск задач.
- Наладить деплой кода. Перевести WordPress‑код на управляемый деплой (Git, CI/CD), исключить ручные правки на нодах.
- Проверить всё под нагрузкой. Прогнать тест‑план: логин, корзина, аплоады, WP‑Cron, плагины кэширования, и только после этого переключать боевой трафик.
Итоги
WordPress на нескольких VDS с балансировщиком — это не магия и не «энтерпрайз‑чернокнижие», а вполне приземлённый набор решений:
- sticky‑сессии на уровне балансировщика, чтобы уменьшить эффект «прыгающих» запросов;
- shared uploads через NFS или S3‑offload, чтобы все ноды видели один и тот же контент;
- централизованные сессии и object cache в Redis или Memcached;
- аккуратный cron и управляемый деплой кода.
Если подойти к этому поэтапно: сначала вынести базу данных, затем общий wp-content/uploads, потом Redis и sticky‑сессии, — кластер перестанет быть «страшным зверем» и станет предсказуемым и масштабируемым окружением для WordPress.
В результате вы получите устойчивый к нагрузке и отказам сайт, который можно горизонтально масштабировать, не боясь рассыпанных сессий, битых аплоадов и хаотичных кронов.


