С оптимизацией nginx есть одна закономерность: конфиги копируют целиком, вместе с чужими значениями и чужими компромиссами. В результате на сервере с двумя гигабайтами памяти оказываются настройки для машины с шестьюдесятью четырьмя, а fastcgi_cache включается на сайте, где половина страниц персональная.
Разберём параметры по смыслу — что каждый из них меняет и от чего зависит правильное значение.
Базовая структура конфига
# /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
worker_rlimit_nofile 65535;
pid /run/nginx.pid;
events {
worker_connections 4096;
use epoll;
multi_accept on;
}
http {
# Базовые настройки
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 1000;
types_hash_max_size 2048;
server_tokens off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Логи
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main buffer=16k;
error_log /var/log/nginx/error.log warn;
# Gzip
include /etc/nginx/conf.d/gzip.conf;
# FastCGI Cache
include /etc/nginx/conf.d/fastcgi-cache.conf;
# Сайты
include /etc/nginx/sites-enabled/*;
}Gzip сжатие
# /etc/nginx/conf.d/gzip.conf
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 1000;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/x-javascript
application/xml
application/xml+rss
application/vnd.ms-fontobject
application/x-font-ttf
font/opentype
image/svg+xml;gzip_comp_level: Значение от 1 до 9. Уровень 6 — оптимальный баланс между сжатием и нагрузкой на CPU. Уровень 9 даёт всего на 1-2% лучше сжатие, но в 2-3 раза медленнее.
FastCGI Cache для PHP
Это самая эффективная и самая опасная настройка в списке. fastcgi_cache кэширует готовый ответ PHP целиком — включая ответ, сформированный для авторизованного пользователя. Без корректного исключения по кукам сессии один посетитель увидит корзину и личные данные другого.
Обязательный минимум: не кэшировать при наличии куки сессии, не кэшировать POST, не кэшировать зону личного кабинета и оформления заказа. И проверить это до выкладки — тем же способом, что и композит: запросить страницу с куками авторизованного пользователя и без них, сравнить ответы.
Приём с обнулением переменной, которую проверяет fastcgi_cache_bypass, здесь работает надёжнее набора отдельных флагов: легко завести флаг, аккуратно его выставлять и нигде не проверять.
# /etc/nginx/conf.d/fastcgi-cache.conf
# Зона кэша
fastcgi_cache_path /var/cache/nginx/fastcgi
levels=1:2
keys_zone=FASTCGI_CACHE:100m
inactive=60m
max_size=1g
use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
fastcgi_cache_use_stale error timeout updating invalid_header http_500 http_503;
fastcgi_cache_background_update on;
fastcgi_cache_lock on;
fastcgi_cache_lock_timeout 5s;
# Не кэшировать для авторизованных
map $http_cookie $no_cache {
default 0;
"~*PHPSESSID" 1;
"~*BITRIX_SM_LOGIN" 1;
"~*BITRIX_SM_UIDH" 1;
}Конфиг сайта с кэшированием
# /etc/nginx/sites-enabled/bitrix.conf
upstream php-fpm {
server unix:/run/php/php8.2-fpm.sock;
keepalive 32;
}
server {
listen 80;
listen 443 ssl http2;
server_name example.com www.example.com;
root /var/www/html;
index index.php index.html;
# SSL
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# HSTS
add_header Strict-Transport-Security "max-age=31536000" always;
# Лимиты
client_max_body_size 100M;
client_body_buffer_size 128k;
client_body_timeout 60s;
client_header_timeout 60s;
# Статика с долгим кэшем
location ~* \.(jpg|jpeg|gif|png|svg|webp|ico|css|js|woff|woff2|ttf|eot|pdf)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
# Открываем CORS для шрифтов
location ~* \.(woff|woff2|ttf|eot)$ {
add_header Access-Control-Allow-Origin "*";
}
}
# Запрет служебных файлов
location ~ /(\.ht|\.git|\.env|\.svn) {
deny all;
return 404;
}
location ~ ^/(bitrix/\.settings|bitrix/php_interface/dbconn) {
deny all;
return 404;
}
# Битрикс urlrewrite
location / {
try_files $uri $uri/ @bitrix;
}
location @bitrix {
fastcgi_pass php-fpm;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/bitrix/urlrewrite.php;
# Кэширование для анонимных
fastcgi_cache FASTCGI_CACHE;
fastcgi_cache_valid 200 301 302 10m;
fastcgi_cache_bypass $no_cache;
fastcgi_no_cache $no_cache;
add_header X-Cache-Status $upstream_cache_status;
}
# PHP
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass php-fpm;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $document_root;
# Таймауты
fastcgi_read_timeout 300s;
fastcgi_send_timeout 300s;
fastcgi_connect_timeout 60s;
# Буферы
fastcgi_buffer_size 128k;
fastcgi_buffers 256 16k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
# Keepalive
fastcgi_keep_conn on;
# Не кэшируем админку
location ~ ^/bitrix/(admin|tools)/ {
fastcgi_pass php-fpm;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_cache off;
}
}
}Оптимизация буферов
# Для больших ответов PHP
fastcgi_buffer_size 128k;
fastcgi_buffers 256 16k;
fastcgi_busy_buffers_size 256k;
fastcgi_temp_file_write_size 256k;
# Proxy буферы (если есть reverse proxy)
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;Размер буферов: Если видите в логах an upstream response is buffered to a temporary file, увеличьте fastcgi_buffers. Но не ставьте слишком большие значения — это съест память.
Кэширование композитного сайта Битрикс
# /etc/nginx/sites-enabled/bitrix-composite.conf
# Проверка наличия композитного кэша
map $uri $composite_cache_file {
default "/bitrix/html_pages${uri}index@.html";
}
server {
# ... базовые настройки ...
location / {
# Сначала проверяем композитный кэш
if (-f "$document_root$composite_cache_file") {
rewrite ^ $composite_cache_file last;
}
try_files $uri $uri/ @bitrix;
}
# Композитные страницы
location ~ ^/bitrix/html_pages/ {
internal;
# Заголовки
add_header X-Bitrix-Composite "nginx";
add_header Cache-Control "no-store, no-cache, must-revalidate";
# Типы контента
types {
text/html html;
}
}
}Rate limiting
# /etc/nginx/conf.d/rate-limit.conf
# Зоны лимитов
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
server {
# ...
# Лимит на API
location /api/ {
limit_req zone=api burst=20 nodelay;
limit_conn conn 10;
# ... fastcgi ...
}
# Строгий лимит на логин
location = /bitrix/admin/ {
limit_req zone=login burst=3;
# ... fastcgi ...
}
}Security headers
# /etc/nginx/conf.d/security.conf
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;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# CSP (настройте под свой сайт)
# add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;Проверка конфигурации
# Проверить синтаксис
nginx -t
# Перезагрузить без даунтайма
nginx -s reload
# Посмотреть текущие соединения
nginx -V 2>&1 | grep -o with-http_stub_status_module
# Если модуль есть, добавить:
location = /nginx_status {
stub_status;
allow 127.0.0.1;
deny all;
}Мониторинг производительности
# Топ медленных запросов из лога
awk '{print $NF}' /var/log/nginx/access.log | sort -rn | head -20
# Статистика кодов ответа
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -rn
# Запросы в секунду
tail -f /var/log/nginx/access.log | pv -l -i10 -r > /dev/nullИтоги
| Параметр | Рекомендация |
|---|---|
| worker_processes | auto (= CPU cores) |
| worker_connections | 2048-4096 |
| keepalive_timeout | 65 |
| gzip_comp_level | 6 |
| fastcgi_buffers | 256 16k |
| fastcgi_cache | 100m-1g |
Приведённые значения — отправная точка, а не универсальные константы. На что смотреть при подборе:
worker_processes — по числу ядер (auto делает это сам), worker_connections — по доступной памяти и характеру нагрузки. Увеличивать второе бесполезно, если упирается PHP-FPM: очередь просто переедет на уровень выше.
gzip_comp_level 6 — разумный компромисс. Девятый уровень даёт единицы процентов выигрыша в размере при заметно большей нагрузке на процессор. Если нужен реальный прирост — включайте brotli, а не поднимайте уровень gzip.
Кэширование статики на 30 дней требует версионирования файлов. Без изменения адреса при изменении содержимого вернувшийся пользователь получит старую вёрстку и не поймёт, почему у него всё сломано.
fastcgi_cache — только с проверенным исключением персональных страниц. Это единственный пункт списка, ошибка в котором приводит к утечке данных, а не к замедлению.
Ограничение частоты запросов настраивайте с burst. Жёсткий лимит без всплеска отсекает нормальных пользователей: обычная страница подтягивает десятки ресурсов почти одновременно.
Заголовки безопасности проверяйте после включения. Content-Security-Policy, добавленный по образцу из статьи, с высокой вероятностью сломает сторонние скрипты — счётчики, чаты, платёжные виджеты. Начинайте с режима отчётов, а не сразу с блокировки.
И порядок работы, который экономит больше всего времени. Меняйте по одному параметру и измеряйте. Конфиг, собранный из десяти изменений сразу, невозможно откатить осмысленно: когда что-то ломается, вы не знаете, какая именно строка виновата. nginx -t перед каждой перезагрузкой и curl -I после — минимальный цикл проверки.
Комментарии
Система комментариев скоро будет подключена. А пока вы можете написать мне в Telegram или на email.