Nginx Reverse Proxy для WordPress: Пошаговая Настройка

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.ru

Certbot автоматически изменит конфиг 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% случаев это конфликт плагина или темы, и решение там же.