Apache с mod_php — это прошлое десятилетие. Современный продакшн-стек для WordPress выглядит так: Nginx как reverse proxy → PHP-FPM → MySQL/MariaDB. Такая архитектура выдерживает в 5-10 раз большую нагрузку при тех же ресурсах сервера, быстрее отдаёт статику и безопаснее по умолчанию.
В этом руководстве мы пройдём путь от чистого Ubuntu-сервера до работающего WordPress-сайта за Nginx с HTTPS и кэшированием. Все конфиги проверены на реальных проектах и работают из коробки.
Почему Nginx, а не Apache?
Если вы всё ещё используете Apache + mod_php для WordPress, вот три причины перейти на Nginx + PHP-FPM:
- Производительность: Nginx асинхронно обрабатывает тысячи соединений, Apache — по одному потоку на запрос
- Потребление RAM: PHP-FPM держит пул процессов с фиксированным размером, mod_php плодит процессы под каждого посетителя
- Статика: Nginx отдаёт CSS/JS/изображения в 3-5 раз быстрее Apache без дополнительной настройки
Минус только один — нет поддержки .htaccess. Все правила нужно явно прописывать в конфиге Nginx. Но это скорее плюс: конфигурация становится предсказуемой и версионируемой в Git.
Архитектура решения
Прежде чем писать конфиги, разберём схему:
- Nginx — принимает HTTP/HTTPS запросы, отдаёт статику, проксирует PHP-запросы в PHP-FPM
- PHP-FPM — пул процессов PHP, обрабатывающий WordPress
- MariaDB — база данных WordPress
- Redis — object cache для ускорения запросов к БД
Вся связка разворачивается на одном VPS (2 CPU / 4 GB RAM достаточно для сайта до 50 000 посетителей в сутки).
Шаг 1: Подготовка сервера
Обновляем систему и ставим базовые пакеты:
# Обновление системы
sudo apt update && sudo apt upgrade -y
# Установка Nginx, PHP-FPM, MariaDB
sudo apt install -y nginx mariadb-server \
php8.2-fpm php8.2-mysql php8.2-xml php8.2-mbstring \
php8.2-curl php8.2-zip php8.2-gd php8.2-imagick \
php8.2-intl php8.2-bcmath php8.2-redis redis-server
# Запуск и автозагрузка
sudo systemctl enable --now nginx mariadb php8.2-fpm redis-serverБазовая настройка PHP-FPM
Редактируем /etc/php/8.2/fpm/pool.d/www.conf под нагрузку сайта:
; Размер пула процессов (формула: RAM_MB / 40)
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20
pm.max_requests = 500
; Увеличиваем лимиты (подробнее в статье про memory_limit)
php_admin_value[memory_limit] = 512M
php_admin_value[upload_max_filesize] = 64M
php_admin_value[post_max_size] = 64M
php_admin_value[max_execution_time] = 300Если вашему сайту нужно больше 512M памяти, сначала разберитесь с причиной — об этом мы подробно писали в руководстве по увеличению memory_limit в PHP.
# Применяем изменения
sudo systemctl restart php8.2-fpmШаг 2: Базовый конфиг Nginx для WordPress
Создаём файл /etc/nginx/sites-available/wordpress:
server {
listen 80;
server_name custom-code.ru www.custom-code.ru;
root /var/www/wordpress;
index index.php index.html;
# Логи
access_log /var/log/nginx/wordpress.access.log;
error_log /var/log/nginx/wordpress.error.log;
# Размер загружаемых файлов
client_max_body_size 64M;
# Защита от скрытия версии
server_tokens off;
# Основной блок для WordPress
location / {
try_files $uri $uri/ /index.php?$args;
}
# Обработка PHP через PHP-FPM
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
# Буферизация для больших ответов
fastcgi_buffer_size 128k;
fastcgi_buffers 256 16k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
}
# Запрет доступа к скрытым файлам
location ~ /\. {
deny all;
}
# Запрет доступа к wp-config.php
location ~* /(?:uploads|files)/.*\.php$ {
deny all;
}
# Кэширование статики
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
}# Активируем сайт
sudo ln -s /etc/nginx/sites-available/wordpress /etc/nginx/sites-enabled/
sudo rm /etc/nginx/sites-enabled/default
# Проверяем конфиг
sudo nginx -t
# Перезагружаем Nginx
sudo systemctl reload nginxШаг 3: Настройка HTTPS через Let’s Encrypt
Без HTTPS сайт не будет ранжироваться в Google. Ставим Certbot:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d custom-code.ru -d www.custom-code.ruCertbot автоматически изменит конфиг Nginx, добавив SSL-директивы. Но базовых настроек недостаточно — добавляем современные стандарты безопасности:
# Добавьте эти строки в блок server { ... }
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# HSTS (31536000 секунд = 1 год)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Защита от clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;Автопродление сертификата
Certbot автоматически добавляет cron-задачу, но проверим:
sudo certbot renew --dry-runШаг 4: FastCGI кэширование (ускорение в 10-50 раз)
Это главная суперсила Nginx для WordPress. Кэш хранится на уровне веб-сервера, PHP даже не вызывается для закэшированных страниц.
В начале файла /etc/nginx/nginx.conf добавляем:
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=WORDPRESS:100m inactive=60m;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout invalid_header http_500;# Создаём директорию кэша
sudo mkdir -p /var/cache/nginx
sudo chown www-data:www-data /var/cache/nginxВ серверном блоке (в location ~ \.php$) добавляем:
location ~ \.php$ {
# ... (предыдущие директивы)
# Кэширование
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 60m;
fastcgi_cache_bypass $no_cache;
fastcgi_no_cache $no_cache;
# Заголовок для отладки (убрать на проде)
add_header X-FastCGI-Cache $upstream_cache_status;
}
# Исключения из кэша (админка, корзина, залогиненные)
set $no_cache 0;
if ($request_method = POST) {
set $no_cache 1;
}
if ($query_string != "") {
set $no_cache 1;
}
if ($request_uri ~* "/wp-admin/|/xmlrpc.php|wp-.*.php|/feed/|index.php|sitemap") {
set $no_cache 1;
}
if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_no_cache|wordpress_logged_in") {
set $no_cache 1;
}После включения кэша вы увидите заголовок X-FastCGI-Cache: HIT при повторных запросах. Это значит, что страница отдана из кэша без обращения к PHP.
Шаг 5: Защита от ботов и брутфорса
WordPress — любимая цель ботов. Добавляем защиту от перебора пароля и парсинга:
# Ограничение скорости запросов к wp-login.php
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
location = /wp-login.php {
limit_req zone=login burst=3 nodelay;
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
# Блокировка xmlrpc.php (если не используете мобильное приложение)
location = /xmlrpc.php {
deny all;
}
# Блокировка по User-Agent
if ($http_user_agent ~* (scrapy|curl|wget|python-requests|java|httrack) ) {
return 403;
}
# Запрет прямого доступа к PHP в uploads
location ~* /wp-content/uploads/.*\.php$ {
deny all;
}Шаг 6: Redis Object Cache
FastCGI кэширует страницы, а Redis кэширует запросы к базе данных. В связке они дают феноменальный результат.
# Устанавливаем плагин в WordPress
cd /var/www/wordpress/wp-content/plugins
wget https://downloads.wordpress.org/plugin/redis-cache.latest-stable.zip
unzip redis-cache.latest-stable.zip
rm redis-cache.latest-stable.zipВ wp-config.php добавляем:
define( 'WP_REDIS_HOST', '127.0.0.1' );
define( 'WP_REDIS_PORT', 6379 );
define( 'WP_REDIS_PASSWORD', '' );
define( 'WP_REDIS_DATABASE', 0 );
define( 'WP_CACHE_KEY_SALT', 'custom-code-ru:' );Активируем плагин в админке WordPress → Настройки → Redis → Enable Object Cache.
Шаг 7: Альтернатива — Docker Compose
Если не хотите настраивать всё руками, используйте Docker. Это тот же стек, но изолированный и воспроизводимый:
# docker-compose.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./wordpress:/var/www/html
depends_on:
- php
php:
image: wordpress:php8.2-fpm
volumes:
- ./wordpress:/var/www/html
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wp
WORDPRESS_DB_PASSWORD: strongpass
WORDPRESS_DB_NAME: wordpress
db:
image: mariadb:10.11
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: wordpress
MYSQL_USER: wp
MYSQL_PASSWORD: strongpass
volumes:
db_data:При работе с Docker следите за лимитами памяти — если контейнер упадёт с кодом 137, смотрите наше руководство по OOMKilled.
Чек-лист продакшн-деплоя
- HTTPS настроен, оценка A+ на SSL Labs
- FastCGI кэш работает (проверить через
curl -I) - Redis Object Cache активирован
- xmlrpc.php заблокирован (если не нужен)
- wp-login.php защищён rate limiting
- Автоматические бэкапы БД и файлов
- Мониторинг доступности (UptimeRobot)
- Fail2ban для защиты от брутфорса
- Обновления Ubuntu через unattended-upgrades
Что делать, если проблемы не решаются?
Nginx отдаёт 502 Bad Gateway
Это значит, что Nginx не может достучаться до PHP-FPM. Проверьте:
- Работает ли PHP-FPM:
sudo systemctl status php8.2-fpm - Совпадает ли путь к сокету в конфиге Nginx и в
/etc/php/8.2/fpm/pool.d/www.conf - Права на сокет:
ls -la /run/php/php8.2-fpm.sock(должен бытьwww-data)
Кэш не работает (всегда MISS)
Частые причины:
- Установлены cookie авторизации (админка всегда идёт мимо кэша)
- Плагин WooCommerce или другой динамический контент без правильной настройки исключений
- Не хватает прав на директорию
/var/cache/nginx - В логе
/var/log/nginx/error.logесть ошибки open() failed (13: Permission denied)
Сайт работает медленно даже с кэшем
Если X-FastCGI-Cache: HIT, но время ответа > 500мс, проблема глубже:
- Медленные внешние API (проверьте через Query Monitor)
- Утечка памяти в PHP-FPM — смотрите гайд по memory_limit
- Фронтенд тормозит из-за тяжёлого JS/CSS — оптимизируйте через Lighthouse
- Redis не подключён — object cache отсутствует
После обновления WordPress сломался сайт
Это частая ситуация — смотрите подробный разбор в руководстве по белому экрану смерти WordPress. В 70% случаев это конфликт плагина или темы, и решение там же.