На небольшом VPS с 512 МБ–2 ГБ памяти Nginx заметно ускоряется после нескольких изменений: правильно подобранные воркеры, keepalive с клиентами и бэкендами, сжатие, повторное использование TLS-сессий, HTTP/2 или HTTP/3, заголовки кеширования в браузере и короткий FastCGI- или proxy-кеш. В этой инструкции мы разберём каждый пункт с конфигами для Debian 12/13 и Ubuntu 22.04/24.04 (с пометками для Rocky Linux/AlmaLinux) и покажем, как измерить результат.
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/
Запишите число запросов в секунду и задержку и повторяйте ту же команду после каждого изменения.
Шаг 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_connections | worker_rlimit_nofile |
|---|---|---|
| mini (1 ядро, 512 МБ) | 1024 | 4096 |
| 01 (2 ядра, 1 ГБ) | 2048 | 8192 |
| 02 (3 ядра, 2 ГБ) | 4096 | 16384 |
Сам 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
Шаг 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.
$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 через тикеты.