Тюнинг Nginx для production в 2026: worker’ы, HTTP/2 и HTTP/3, gzip и brotli, кэш и rate limiting — PROG-TIME

Тюнинг Nginx для production в 2026: worker’ы, HTTP/2 и HTTP/3, gzip и brotli, кэш и rate limiting

02.07.2026

Nginx по-прежнему стоит на входе у большинства PHP- и Laravel-проектов: принимает трафик, отдаёт статику, проксирует запросы в php-fpm или application-сервер. Но дефолтная конфигурация из пакета рассчитана на «чтобы работало», а не на «чтобы держало нагрузку». В этой статье собираем практический чек-лист тюнинга Nginx на 2026 год: рабочие процессы, keepalive, сжатие gzip и brotli, HTTP/2 и HTTP/3, кэширование и защита от всплесков запросов. Все примеры — под актуальную стабильную ветку 1.28.x (1.28.1 вышла в декабре 2025), в мейнлайне уже 1.29.

Рабочие процессы и соединения

Базовая пропускная способность Nginx определяется числом рабочих процессов и лимитом соединений на каждый из них. Значение auto заставляет Nginx создать по одному worker на ядро — это оптимально почти всегда, ручные числа нужны редко.

# /etc/nginx/nginx.conf
worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 16384;
    multi_accept on;
    use epoll;
}

Ключевой момент: worker_connections ограничен числом открытых файловых дескрипторов процесса. Поэтому worker_rlimit_nofile должен быть не меньше, чем worker_connections, иначе упрётесь в лимит ОС раньше, чем в настройку Nginx. Директива multi_accept on позволяет воркеру принимать сразу несколько новых соединений за один цикл, а epoll — это эффективный механизм обработки событий в Linux (Nginx выбирает его сам, но указать явно не мешает). Теоретический максимум одновременных клиентов — worker_processes × worker_connections, но помните, что проксирование в бэкенд тоже расходует по соединению.

Keepalive, sendfile и таймауты

Переиспользование TCP-соединений снимает накладные расходы на постоянные рукопожатия. Отдача статики выигрывает от sendfile в связке с tcp_nopush, а вот интерактивные ответы — от tcp_nodelay.

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;

    keepalive_timeout 65;
    keepalive_requests 1000;

    client_max_body_size 32m;
    reset_timedout_connection on;
    server_tokens off;
}

Отдельно стоит настроить keepalive к бэкенду — это одна из самых недооценённых оптимизаций. По умолчанию Nginx открывает новое соединение к php-fpm или upstream на каждый запрос. Пул постоянных соединений заметно снижает задержку:

upstream php_backend {
    server 127.0.0.1:9000;
    keepalive 32;
}

server {
    location ~ \.php$ {
        fastcgi_pass php_backend;
        fastcgi_keep_conn on;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

Директива server_tokens off убирает номер версии Nginx из заголовков и страниц ошибок — маленькая, но полезная мера против автоматического сканирования уязвимостей.

Сжатие: gzip и brotli

Gzip встроен в Nginx и включается парой строк. Важно не сжимать то, что уже сжато (картинки, видео, архивы), и не тратить CPU на крохотные ответы.

gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_vary on;
gzip_proxied any;
gzip_types
    text/plain text/css application/json
    application/javascript text/xml application/xml
    image/svg+xml application/font-woff2;

Brotli при сравнимой нагрузке на CPU даёт на текстовых ресурсах в среднем на 15–20% меньший размер, чем gzip. В стандартную поставку он не входит — нужен динамический модуль ngx_brotli от Google. В сборках 1.28.x для популярных дистрибутивов он часто уже включён; если нет, модуль собирается из исходников и подключается как динамический.

# подключение динамического модуля в начале nginx.conf
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;

# в блоке http
brotli on;
brotli_comp_level 5;
brotli_static on;
brotli_types
    text/plain text/css application/json
    application/javascript image/svg+xml;

Опция brotli_static on позволяет отдавать заранее сжатые файлы .br, если они лежат рядом с оригиналом, — это удобно для собранных ассетов фронтенда (Vite, webpack), где сжатие делается один раз на этапе билда, а не на каждый запрос.

HTTP/2 и HTTP/3 (QUIC)

HTTP/2 давно стал стандартом де-факто и включается одной директивой. Обратите внимание на современный синтаксис: с версии 1.25.1 http2 задаётся отдельной строкой, а не параметром listen.

server {
    listen 443 ssl;
    http2 on;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
}

HTTP/3 работает поверх QUIC (UDP) и доступен в Nginx начиная с 1.25.0, а в стабильной ветке 1.28.x идёт «из коробки» в бинарных пакетах. Для сборки с QUIC рекомендуется OpenSSL 3.5 и выше; QUIC требует TLS 1.3. Разработчики Nginx всё ещё помечают реализацию как относительно новую, поэтому на критичном проде включайте её осознанно и с мониторингом. Ключевой нюанс — заголовок Alt-Svc, которым сервер сообщает браузеру о поддержке HTTP/3, иначе клиент так и останется на HTTP/2.

server {
    listen 443 ssl;      # TCP для HTTP/1.1 и HTTP/2
    listen 443 quic reuseport;  # UDP для HTTP/3
    http2 on;
    http3 on;

    ssl_protocols TLSv1.3;

    add_header Alt-Svc 'h3=":443"; ma=86400';
}

Не забудьте открыть UDP-порт 443 в firewall — про это забывают чаще всего, и HTTP/3 «молча» не поднимается, хотя конфиг корректен.

Кэширование статики и FastCGI-кэш

Для статических ассетов с хешем в имени файла можно смело выставлять «вечный» срок жизни в кэше браузера — при изменении контента поменяется имя файла.

location ~* \.(?:css|js|woff2|jpg|jpeg|png|gif|svg|webp|avif)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
    access_log off;
}

Если бэкенд отдаёт страницы, которые редко меняются, включите FastCGI-кэш — Nginx будет держать готовый ответ php-fpm и отдавать его без обращения к PHP. Это снимает с приложения основную массу повторяющихся запросов.

# в http
fastcgi_cache_path /var/cache/nginx levels=1:2
    keys_zone=phpcache:20m max_size=1g
    inactive=60m use_temp_path=off;

# в location ~ \.php$
fastcgi_cache phpcache;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_valid 200 301 302 10m;
fastcgi_cache_use_stale error timeout updating http_500;
fastcgi_cache_background_update on;
add_header X-FastCGI-Cache $upstream_cache_status;

Заголовок X-FastCGI-Cache со значениями HIT/MISS/BYPASS очень помогает при отладке — сразу видно, попал запрос в кэш или нет. Для авторизованных пользователей и POST-запросов кэш нужно обходить через fastcgi_cache_bypass и fastcgi_no_cache по наличию cookie сессии.

Rate limiting и защита от всплесков

Даже без полноценного WAF встроенные лимиты Nginx закрывают базовые сценарии перебора паролей и флуда. Зоны описываются в http, применяются — в нужных location.

# в http
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/s;
limit_conn_zone $binary_remote_addr zone=perip:10m;

# в server / location
location /login {
    limit_req zone=login burst=10 nodelay;
    limit_conn perip 20;
}

Параметр burst задаёт размер очереди для коротких всплесков, а nodelay обрабатывает их сразу, а не растягивает во времени. Отдельная зона limit_conn ограничивает число одновременных соединений с одного IP — это защищает от медленных клиентов, удерживающих соединения. Начинайте с мягких лимитов и следите за логами: слишком строгие правила бьют по легитимным пользователям за NAT.

Итоги

Тюнинг Nginx — это не один магический параметр, а сумма мелких настроек: воркеры и дескрипторы под нагрузку, keepalive к клиенту и к бэкенду, gzip плюс brotli для трафика, HTTP/2 и HTTP/3 для скорости, кэш статики и FastCGI-кэш для разгрузки PHP, лимиты запросов для защиты. Внедряйте изменения по одному и обязательно проверяйте конфиг перед перезагрузкой командой nginx -t, а применяйте через nginx -s reload без разрыва соединений. И измеряйте результат — ab, wrk или k6 покажут, действительно ли настройка дала эффект именно на вашем профиле нагрузки, а не только в теории.