Центр помощи

Пошаговые руководства по серверам, сетям и аккаунту.

Linux-сервер

Оптимизация Nginx на небольшом VPS: воркеры, keepalive, Gzip/Brotli, HTTP/2, HTTP/3 и кеширование

Оптимизация Nginx на небольшом VPS: воркеры, keepalive, Gzip/Brotli, HTTP/2, HTTP/3 и кеширование

На небольшом VPS с 512 МБ–2 ГБ памяти Nginx заметно ускоряется после нескольких изменений: правильно подобранные воркеры, keepalive с клиентами и бэкендами, сжатие, повторное использование TLS-сессий, HTTP/2 или HTTP/3, заголовки кеширования в браузере и короткий FastCGI- или proxy-кеш. В этой инструкции мы разберём каждый пункт с конфигами для Debian 12/13 и Ubuntu 22.04/24.04 (с пометками для Rocky Linux/AlmaLinux) и покажем, как измерить результат.

Перед началом убедитесь, что HTTPS уже работает: см. Как установить SSL-сертификат. Сначала сделайте резервную копию конфигурации: sudo cp -a /etc/nginx /root/nginx-backup-$(date +%F).

Шаг 1: Измерьте исходные показатели

Сначала измерьте — так вы поймёте, что действительно помогло. Для одного запроса curl покажет, на что уходит время:

curl -so /dev/null -w 'dns %{time_namelookup}s  connect %{time_connect}s  tls %{time_appconnect}s  ttfb %{time_starttransfer}s  total %{time_total}s  http/%{http_version}\n' https://example.com/

connect минус dns — это примерно сетевой RTT, tls — рукопожатие, а ttfb (время до первого байта) минус tls — сколько сервер готовил страницу. Высокий ttfb на PHP-странице указывает на приложение, и именно здесь кеширование (шаг 7) даёт больше всего.

Для пропускной способности под нагрузкой используйте wrk, ab или hey, лучше с другой машины, чтобы инструмент теста не отнимал CPU у Nginx:

# Debian/Ubuntu: sudo apt install wrk apache2-utils
# Rocky/Alma:    sudo dnf install httpd-tools
wrk -t2 -c50 -d30s https://example.com/
ab -n 2000 -c 20 -k https://example.com/
hey -n 2000 -c 50 https://example.com/

Запишите число запросов в секунду и задержку и повторяйте ту же команду после каждого изменения.

Нагружайте только свои сайты и делайте тесты короткими. На облачных VPS JUSTG короткие всплески CPU до 100% допустимы, но если средняя загрузка CPU за час превышает 50%, она автоматически ограничивается до 50%.

Шаг 2: Проверьте версию Nginx и модули

nginx -v
nginx -V 2>&1 | tr ' ' '\n' | grep -E 'http_v2|http_v3'
ls /etc/nginx/modules-enabled/ 2>/dev/null
nproc; free -m
grep -nE 'worker_|gzip|keepalive_timeout|access_log|ssl_session' /etc/nginx/nginx.conf

От версии зависит доступный синтаксис. В Debian 12 — 1.22, в Debian 13 — 1.26, в Ubuntu 22.04 — 1.18, в Ubuntu 24.04 — 1.24. Для HTTP/3 нужен Nginx 1.25 или новее, собранный с --with-http_v3_module; если в пакете вашего дистрибутива его нет, официальный репозиторий nginx.org даёт актуальные сборки с этим модулем. Последняя команда показывает настройки, которые уже есть в nginx.conf: перед шагом 4 закомментируйте там gzip on;, keepalive_timeout, access_log и строки ssl_session_*, иначе nginx -t сообщит о дублирующейся директиве или каждый запрос будет записываться в лог дважды.

Шаг 3: Подберите параметры воркеров

# /etc/nginx/nginx.conf - main context (outside http {})
worker_processes auto;
worker_rlimit_nofile 8192;

events {
    worker_connections 2048;
}

worker_processes auto запускает по одному воркеру на ядро CPU. Каждый воркер одновременно держит до worker_connections соединений, а проксируемый запрос занимает два (клиент и бэкенд). worker_rlimit_nofile поднимает лимит открытых файлов для воркеров; держите его хотя бы вдвое больше worker_connections. Рекомендуемые значения:

Тарифworker_connectionsworker_rlimit_nofile
mini (1 ядро, 512 МБ)10244096
01 (2 ядра, 1 ГБ)20488192
02 (3 ядра, 2 ГБ)409616384

Сам Nginx тратит на соединение совсем немного памяти. На небольших тарифах реальным ограничением обычно становятся PHP-FPM или ваше приложение, поэтому размер их пулов подбирайте под доступную память.

Шаг 4: Keepalive, сжатие, TLS-сессии, кеш файлов и логи

Вынесите общие настройки уровня http в один файл. И Debian/Ubuntu, и Rocky/Alma подключают /etc/nginx/conf.d/*.conf внутри http {}:

sudo tee /etc/nginx/conf.d/00-performance.conf > /dev/null <<'EOF'
# Loaded inside http {} by nginx.conf

# Keepalive with clients
keepalive_timeout 30s;
keepalive_requests 1000;

# Compression
gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_vary on;
gzip_proxied any;
gzip_types text/plain text/css text/xml text/javascript application/javascript
           application/json application/xml application/rss+xml image/svg+xml;

# TLS session reuse
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

# Cache open file descriptors and metadata
open_file_cache max=2000 inactive=60s;
open_file_cache_valid 60s;
open_file_cache_min_uses 2;
open_file_cache_errors on;

# Buffered access log
access_log /var/log/nginx/access.log combined buffer=64k flush=5s;
EOF
  • Keepalive: браузер использует одно соединение для многих запросов и не тратит каждый раз время на TCP- и TLS-рукопожатие. 30 секунд — хороший баланс для небольшого сервера; очень длинные тайм-ауты лишь держат открытыми простаивающие соединения.
  • Gzip: уровень 5 даёт основную часть сжатия при небольшой нагрузке на CPU. text/html сжимается всегда, поэтому в списке его нет. Не добавляйте изображения JPEG, PNG и архивы: они уже сжаты.
  • Кеш TLS-сессий: вернувшиеся посетители возобновляют TLS-сессию без полного рукопожатия. 10 МБ вмещают примерно 40 000 сессий.
  • open_file_cache: хранит дескрипторы и метаданные часто отдаваемых файлов. После деплоя изменённые файлы могут подхватываться с задержкой до open_file_cache_valid (60 с).
  • Буферизованный access-лог: Nginx пишет лог блоками по 64 КБ или раз в 5 секунд, а не на каждый запрос, что сокращает запись на диск. Записи появляются с небольшой задержкой.

Brotli (по желанию)

Brotli сжимает текстовые ресурсы примерно на 15–20% лучше gzip, но он не входит в стандартный Nginx. В некоторых выпусках Debian и Ubuntu он есть в виде динамического модуля; сначала проверьте:

apt-cache search libnginx-mod-http-brotli
sudo apt install libnginx-mod-http-brotli-filter libnginx-mod-http-brotli-static

Если поиск ничего не нашёл, в вашем выпуске модуль не упакован; в базовых репозиториях Rocky/Alma его тоже нет. В этом случае просто оставайтесь на gzip — этого вполне достаточно. Если модуль установился, добавьте:

# Add to /etc/nginx/conf.d/00-performance.conf only if the module is installed
brotli on;
brotli_comp_level 5;
brotli_types text/plain text/css text/xml text/javascript application/javascript
             application/json application/xml application/rss+xml image/svg+xml;

Шаг 5: Keepalive к бэкенду (обратный прокси)

Если Nginx проксирует запросы в приложение (Node.js, Python, Go и т. п.), держите пул простаивающих соединений к нему, а не открывайте новое на каждый запрос:

upstream app_backend {
    server 127.0.0.1:3000;
    keepalive 16;
}

server {
    # ... listen, server_name, certificates ...
    location / {
        proxy_pass http://app_backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        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;
    }
}

keepalive 16 — число простаивающих соединений на каждый воркер. Keepalive к бэкенду работает только с proxy_http_version 1.1 и пустым заголовком Connection. Если приложение использует WebSocket, задайте для этого location собственные заголовки Upgrade/Connection.

Шаг 6: Включите HTTP/2 и HTTP/3

HTTP/2 передаёт много запросов по одному соединению. HTTP/3 работает поверх QUIC (UDP) и лучше справляется с потерями пакетов и сменой сети, что помогает мобильным и удалённым посетителям. Для Nginx 1.25.1 и новее:

server {
    listen 443 ssl;
    listen 443 quic reuseport;
    http2 on;
    http3 on;
    server_name example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

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

    root /var/www/example.com;
}
  • reuseport можно указать только один раз для пары адрес–порт во всех блоках server. Другие сайты на том же IP используют просто listen 443 quic;.
  • QUIC всегда работает на TLS 1.3, поэтому не отключайте TLSv1.3.
  • Заголовок Alt-Svc сообщает браузеру, что доступен HTTP/3: первый визит идёт по HTTP/2, а следующие запросы переходят на HTTP/3.
  • Если вы используете IPv6, добавьте такие же строки с [::]:443.

В старых версиях Nginx (пакеты Ubuntu 22.04/24.04 и Debian 12) директивы http2 on; нет, а HTTP/3 недоступен. Включите HTTP/2 в строке listen:

listen 443 ssl http2;

Для HTTP/3 также нужно открыть UDP-порт 443 в файрволе (см. настройку файрвола):

# Ubuntu/Debian with ufw
sudo ufw allow 443/udp

# Rocky/Alma with firewalld
sudo firewall-cmd --permanent --add-port=443/udp
sudo firewall-cmd --reload

Проверьте заголовок, а если ваш curl собран с поддержкой HTTP/3 (curl -V показывает HTTP3), проверьте и само подключение. Иначе посмотрите столбец Protocol в инструментах разработчика браузера — там должно быть h3:

curl -sI https://example.com/ | grep -i alt-svc
curl -sI --http3-only https://example.com/ | head -n 1
Если сайт работает за CDN-прокси, например Cloudflare, посетители подключаются к CDN, и HTTP/3 для них обеспечивает CDN. Тогда HTTP/3 на исходном сервере почти ничего не меняет.

Шаг 7: Кешируйте статические файлы и динамические страницы

Заголовки кеширования статики в браузере

# Static files: let browsers cache them
location ~* \.(?:css|js|mjs|woff2|svg|png|jpe?g|gif|webp|avif|ico)$ {
    expires 30d;
    access_log off;
    try_files $uri =404;
}

# Versioned assets (file name contains a hash, e.g. app.3f9a1c.js)
location ^~ /assets/ {
    add_header Cache-Control "public, max-age=31536000, immutable";
    add_header Alt-Svc 'h3=":443"; ma=86400' always;
    access_log off;
}

Правило immutable на год применяйте только к файлам, имя которых меняется вместе с содержимым, иначе посетители будут видеть старые версии. Учтите одно правило Nginx: если в location есть хоть одна директива add_header, он перестаёт наследовать add_header из блока server. Поэтому Alt-Svc там повторяется; так же поступайте с HSTS и другими заголовками безопасности.

Создайте каталоги кеша

sudo mkdir -p /var/cache/nginx/fastcgi /var/cache/nginx/proxy
sudo chown -R www-data:www-data /var/cache/nginx    # nginx:nginx on Rocky/Alma

FastCGI-микрокеш для PHP

Даже 10 секунд кеша для вывода PHP превращают сотни запусков PHP в секунду на популярной странице в один запуск раз в 10 секунд, а контент остаётся почти актуальным:

# /etc/nginx/conf.d/10-fastcgi-cache.conf (http context)
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2 keys_zone=PHPCACHE:32m
                   max_size=256m inactive=10m use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
# Inside the server {} block of the PHP site
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "")    { set $skip_cache 1; }
if ($http_cookie ~* "PHPSESSID|wordpress_logged_in|wp-postpass|comment_author|woocommerce_items_in_cart") { set $skip_cache 1; }
if ($request_uri ~* "^/(wp-admin|wp-login\.php|cart|checkout|my-account)") { set $skip_cache 1; }

location ~ \.php$ {
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;

    fastcgi_cache PHPCACHE;
    fastcgi_cache_valid 200 301 302 10s;
    fastcgi_cache_use_stale error timeout updating http_500 http_503;
    fastcgi_cache_background_update on;
    fastcgi_cache_lock on;
    fastcgi_cache_bypass $skip_cache;
    fastcgi_no_cache $skip_cache;

    add_header X-Cache $upstream_cache_status always;
    add_header Alt-Svc 'h3=":443"; ma=86400' always;
}

Укажите сокет своей версии PHP: php8.2-fpm в Debian 12, php8.4-fpm в Debian 13, php8.1-fpm в Ubuntu 22.04, php8.3-fpm в Ubuntu 24.04. В Rocky/Alma используйте include fastcgi_params; вместе с fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; и сокет unix:/run/php-fpm/www.sock. Авторизованные пользователи, POST-запросы, строки запроса, корзина и админка никогда не кешируются. Кроме того, Nginx не кеширует ответы, которые устанавливают cookie или содержат Cache-Control: private или no-store.

Никогда не кешируйте страницы с персональными данными. Если у вашего приложения своя cookie авторизации, добавьте её имя в строку с $http_cookie и перед запуском проверьте сайт под авторизованным пользователем.

proxy_cache для приложения

# http context, e.g. /etc/nginx/conf.d/10-proxy-cache.conf
proxy_cache_path /var/cache/nginx/proxy levels=1:2 keys_zone=APPCACHE:16m
                 max_size=512m inactive=60m use_temp_path=off;

# Add inside location / of the upstream example above
    proxy_cache APPCACHE;
    proxy_cache_key $scheme$host$request_uri;
    proxy_cache_valid 200 301 1m;
    proxy_cache_valid 404 10s;
    proxy_cache_use_stale error timeout updating http_502 http_503 http_504;
    proxy_cache_background_update on;
    proxy_cache_lock on;
    proxy_cache_bypass $http_authorization $cookie_session;
    proxy_no_cache $http_authorization $cookie_session;
    add_header X-Cache $upstream_cache_status always;

Запросы с заголовком Authorization или cookie session (переименуйте под своё приложение) идут мимо кеша. Если приложение само отправляет заголовки Cache-Control или Expires, Nginx следует им, а не proxy_cache_valid. В Rocky/Alma SELinux не даёт Nginx подключаться к локальному порту и может блокировать каталог кеша, пока вы не выполните:

sudo setsebool -P httpd_can_network_connect 1
sudo restorecon -Rv /var/cache/nginx

Шаг 8: Проверьте и перезагрузите конфигурацию

sudo nginx -t && sudo systemctl reload nginx
curl -sI https://example.com/ | grep -iE 'x-cache|content-encoding|alt-svc'

nginx -t проверяет синтаксис до применения изменений; reload применяет новую конфигурацию без разрыва соединений. Запросите кешируемую страницу дважды: X-Cache должен смениться с MISS на HIT. Затем повторите замеры из шага 1 и сравните. Если nginx -t выдаёт ошибку, он указывает файл и строку — исправьте её или восстановите резервную копию из /root/.

FAQ

Какое изменение даёт наибольший эффект?

Для PHP-сайтов вроде WordPress — безусловно, FastCGI-микрокеш: большинство запросов перестают доходить до PHP. Для статических сайтов — сжатие и заголовки кеширования. Настройка воркеров заметна в основном при большом числе одновременных посетителей.

Как очистить кеш после обновления контента?

Удалите файлы кеша и перезагрузите Nginx: sudo rm -rf /var/cache/nginx/fastcgi/* (или proxy/*), затем sudo systemctl reload nginx. С 10-секундным микрокешем обычно достаточно просто подождать.

Хватит ли VPS с 512 МБ для Nginx?

Для самого Nginx — да: статику и кешированные страницы он отдаёт, расходуя очень мало памяти. На тарифе mini ограничением обычно становятся PHP-FPM или база данных, поэтому держите их пулы небольшими, а нагрузку пусть берёт на себя кеш. Если нужна помощь, откройте тикет: в JUSTG работает AI-поддержка 24/7 через тикеты.

Помог ли вам данный ответ?
Центр помощи
Linux-сервер Вход по SSH-ключу в Linux: настройка ключей ed25519 и отключение входа по паролю Любой сервер с публичным IPv4 через несколько минут после запуска начинают атаковать боты, перебирающие SSH-пароли. В этой инструкции… Linux-сервер Как безопасно сменить порт SSH и пароль root на Linux VPS Перенос SSH с порта 22 не остановит целенаправленную атаку, но уберёт из логов большую часть автоматического сканирования, а смена пароля… Linux-сервер Настройка файрвола в Linux: UFW и firewalld на VPS (IPv4 и IPv6) Только что установленный Linux-сервер отвечает на любом порту, который открыла какая-либо служба, поэтому файрвол — одна из первых вещей,… Linux-сервер Как добавить swap на Linux VPS (тарифы с 512 МБ и 1 ГБ ОЗУ) На облачном сервере с 512 МБ или 1 ГБ ОЗУ база данных, пул PHP-FPM и обновление пакетов легко исчерпывают память одновременно. Тогда OOM… Linux-сервер Диск заполнен в Linux: как найти и освободить место на VPS Ошибка «No space left on device» ломает на сервере почти всё: базы данных перестают писать, сайты отдают ошибки, обновления пакетов не…
AlipayUnionPayVISAMastercardPayPalUSDTstripe