Nginx
Nginx
Nginx — веб-сервер, reverse proxy и балансировщик нагрузки. Он принимает HTTP/HTTPS-запросы, раздаёт статические файлы, передаёт запросы приложениям, сжимает и кеширует ответы.
Клиент → Nginx → приложение
├── backend-1:8000
├── backend-2:8000
└── статические файлыУстановка и управление
Debian/Ubuntu
sudo apt update
sudo apt install nginxFedora/RHEL
sudo dnf install nginxНа старых системах может использоваться yum:
sudo yum install nginxArch Linux
sudo pacman -S nginxЗапуск и включение автозапуска:
sudo systemctl enable --now nginx
systemctl status nginxОсновные команды:
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx
sudo systemctl enable nginx
sudo systemctl disable nginxДля обычного изменения конфигурации предпочтителен reload: Nginx перечитывает настройки без резкого завершения текущих соединений.
Перед применением изменений всегда проверяйте конфигурацию:
sudo nginx -t
sudo nginx -t && sudo systemctl reload nginxПолная конфигурация с раскрытыми include:
sudo nginx -TВерсия и параметры сборки:
nginx -v
nginx -VФайлы конфигурации
Основной файл:
/etc/nginx/nginx.confРаспространённые пути:
/etc/nginx/conf.d/ дополнительные конфигурации
/etc/nginx/sites-available/ доступные сайты в Debian/Ubuntu
/etc/nginx/sites-enabled/ включённые сайты в Debian/Ubuntu
/var/log/nginx/ журналы
/usr/share/nginx/html/ стандартная статика
/var/www/ каталоги сайтовНа Debian/Ubuntu сайт часто включают символической ссылкой:
sudo ln -s /etc/nginx/sites-available/example.conf \
/etc/nginx/sites-enabled/example.conf
sudo nginx -t && sudo systemctl reload nginxУпрощённый /etc/nginx/nginx.conf:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}Пользователь процесса может называться www-data, nginx или иначе — это зависит от дистрибутива. Файлы из conf.d и sites-enabled обычно уже подключаются внутри блока http, поэтому второй блок http в них создавать не нужно.
Блок server
server описывает виртуальный сервер: порт, домены, корневой каталог, журналы и обработчики URI.
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example/public;
index index.html;
access_log /var/log/nginx/example.access.log;
error_log /var/log/nginx/example.error.log warn;
location / {
try_files $uri $uri/ =404;
}
}Основные директивы:
| Директива | Назначение |
|---|---|
listen |
Адрес и порт прослушивания |
server_name |
Доменные имена виртуального сервера |
root |
Корневой каталог сайта |
index |
Индексные файлы |
access_log |
Журнал запросов |
error_log |
Журнал ошибок |
Сервер по умолчанию:
server {
listen 80 default_server;
server_name _;
return 444;
}default_server относится к паре адрес/порт. Имя _ лишь часто используется как заведомо неподходящее имя и не является специальной маской.
Блок location
location выбирает обработчик по URI.
Префикс
location /images/ {
root /var/www/example/public;
}Точное совпадение
location = /health {
access_log off;
default_type text/plain;
return 200 "ok\n";
}Приоритетный префикс
location ^~ /static/ {
root /var/www/example/public;
}^~ прекращает поиск среди регулярных location, если этот префикс выбран.
Регулярное выражение
location ~ \.php$ {
# С учётом регистра
}
location ~* \.(jpg|jpeg|png|gif|svg|webp)$ {
# Без учёта регистра
}Упрощённый порядок выбора:
- Точное совпадение
=. - Самый длинный префикс.
- Если префикс имеет
^~, используется он. - Иначе проверяются регулярные выражения по порядку записи.
- Если regex не совпал, используется лучший префикс.
root, alias, try_files
root добавляет URI к пути:
location /media/ {
root /srv/www;
}Запрос /media/photo.jpg ищется как /srv/www/media/photo.jpg.
alias заменяет совпавшую часть URI:
location /media/ {
alias /srv/storage/;
}Тот же запрос ищется как /srv/storage/photo.jpg. Для каталогов согласовывайте завершающий / у location и alias.
try_files проверяет существование файлов по порядку:
location / {
try_files $uri $uri/ =404;
}Для SPA:
location / {
try_files $uri $uri/ /index.html;
}Раздача статики
server {
listen 80;
server_name static.example.com;
root /var/www/static;
index index.html;
location / {
try_files $uri $uri/ =404;
}
location ~* \.(css|js|jpg|jpeg|png|gif|svg|webp|ico|woff2?)$ {
try_files $uri =404;
expires 30d;
add_header Cache-Control "public";
}
}Nginx должен иметь право чтения файла и право прохода x для всех родительских каталогов. Типичные права для неизменяемой статики:
sudo find /var/www/static -type d -exec chmod 755 {} +
sudo find /var/www/static -type f -exec chmod 644 {} +Не используйте chmod -R 777. На системах с SELinux также проверяйте контексты безопасности.
Reverse proxy
Минимальный пример:
server {
listen 80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
}
}Практический вариант:
location / {
proxy_http_version 1.1;
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;
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_pass http://127.0.0.1:8000;
}| Заголовок | Значение |
|---|---|
Host |
Исходное имя хоста |
X-Real-IP |
Адрес клиента для Nginx |
X-Forwarded-For |
Цепочка адресов клиента и прокси |
X-Forwarded-Proto |
Исходная схема http или https |
Приложение должно доверять этим заголовкам только от известных прокси.
Завершающий / в proxy_pass
location /api/ {
proxy_pass http://127.0.0.1:8000/;
}Запрос /api/users обычно передаётся как /users.
location /api/ {
proxy_pass http://127.0.0.1:8000;
}Здесь upstream обычно получает /api/users.
WebSocket
В контексте http:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}В server:
location /ws/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 1h;
proxy_pass http://127.0.0.1:8000;
}Балансировка нагрузки: upstream
upstream объявляется в контексте http:
upstream app_backend {
server 10.0.0.11:8000;
server 10.0.0.12:8000;
}
server {
listen 80;
server_name app.example.com;
location / {
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;
proxy_pass http://app_backend;
}
}По умолчанию применяется round-robin.
Вес
upstream app_backend {
server 10.0.0.11:8000 weight=3;
server 10.0.0.12:8000 weight=1;
}Наименьшее число соединений
upstream app_backend {
least_conn;
server 10.0.0.11:8000;
server 10.0.0.12:8000;
}Привязка по IP
upstream app_backend {
ip_hash;
server 10.0.0.11:8000;
server 10.0.0.12:8000;
}ip_hash имеет ограничения при NAT и прокси и не заменяет общее хранилище сессий.
Резерв и пассивная проверка
upstream app_backend {
server 10.0.0.11:8000 max_fails=3 fail_timeout=30s;
server 10.0.0.12:8000 max_fails=3 fail_timeout=30s;
server 10.0.0.20:8000 backup;
}Open Source Nginx обычно обнаруживает проблемы пассивно, во время реальных запросов. Активные health checks зависят от редакции и модулей.
Gzip-сжатие
Обычно настраивается в http:
gzip on;
gzip_comp_level 5;
gzip_min_length 1024;
gzip_vary on;
gzip_proxied any;
gzip_types
text/plain
text/css
text/xml
application/json
application/javascript
application/xml
image/svg+xml;text/html сжимается при включённом gzip по умолчанию. JPEG, PNG, WebP, архивы и видео уже сжаты, поэтому добавлять их обычно бессмысленно.
Проверка:
curl -I -H 'Accept-Encoding: gzip' https://example.com/app.jsОжидаемый заголовок:
Content-Encoding: gzipКеширование статики в браузере
location ~* \.(css|js|jpg|jpeg|png|gif|svg|webp|ico|woff2?)$ {
expires 30d;
add_header Cache-Control "public";
try_files $uri =404;
}Для файлов с хешем содержимого в имени:
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}immutable безопасен, если URI меняется при изменении содержимого. HTML обычно не кешируют надолго:
location = /index.html {
expires -1;
add_header Cache-Control "no-cache";
}no-cache разрешает хранение, но требует проверки актуальности. no-store запрещает хранение.
Proxy cache
Зона объявляется в http:
proxy_cache_path /var/cache/nginx/app
levels=1:2
keys_zone=app_cache:20m
max_size=2g
inactive=60m
use_temp_path=off;Использование:
location /public-api/ {
proxy_cache app_cache;
proxy_cache_methods GET HEAD;
proxy_cache_valid 200 10m;
proxy_cache_valid 404 1m;
proxy_cache_lock on;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cache-Status $upstream_cache_status always;
proxy_pass http://app_backend;
}Основные значения $upstream_cache_status:
| Значение | Смысл |
|---|---|
MISS |
Записи не было |
HIT |
Ответ взят из кеша |
BYPASS |
Кеш обойдён |
EXPIRED |
Запись устарела |
STALE |
Выдана устаревшая запись |
UPDATING |
Запись обновляется |
Не кешируйте без строгих правил личные кабинеты, корзины и ответы с персональными данными. Для запросов с авторизацией или сессионными cookie кеш обычно отключают:
map $http_authorization $skip_auth_cache {
default 1;
"" 0;
}
location /api/ {
proxy_cache app_cache;
proxy_cache_bypass $skip_auth_cache;
proxy_no_cache $skip_auth_cache;
proxy_pass http://app_backend;
}Если ответ зависит от cookie, языка или заголовков, это должно учитываться условиями обхода и ключом кеша.
Логи Nginx
Стандартные журналы:
/var/log/nginx/access.log
/var/log/nginx/error.logСобственный формат объявляется в http:
log_format main '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time urt=$upstream_response_time '
'upstream=$upstream_addr cache=$upstream_cache_status';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;Полезные переменные:
| Переменная | Значение |
|---|---|
$remote_addr |
IP клиента с точки зрения Nginx |
$request |
Строка запроса |
$status |
HTTP-код ответа |
$request_time |
Полное время обработки |
$upstream_response_time |
Время ответа backend |
$upstream_addr |
Выбранный backend |
$upstream_cache_status |
Состояние кеша |
Просмотр:
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log
sudo journalctl -u nginx -f
sudo journalctl -u nginx --since todayНе записывайте в логи пароли, токены, Authorization и чувствительные параметры URL. Ротация обычно настраивается через /etc/logrotate.d/nginx.
Диагностика
403 Forbidden
Проверьте права на файл и все родительские каталоги, root/alias, autoindex, правила allow/deny и SELinux:
namei -l /var/www/example/public/index.html
sudo tail -n 100 /var/log/nginx/error.log404 Not Found
Проверьте существование файла, root, alias, try_files и преобразование URI в proxy_pass.
502 Bad Gateway
Nginx не смог получить корректный ответ от backend:
curl -v http://127.0.0.1:8000/
ss -lntp
sudo journalctl -u nginx -n 100504 Gateway Timeout
Backend не ответил вовремя. Следует искать причину медленной работы приложения, а не только увеличивать proxy_read_timeout.
Полный пример
Файл подключается из контекста http:
upstream api_backend {
least_conn;
server 127.0.0.1:8001 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8002 max_fails=3 fail_timeout=30s;
}
proxy_cache_path /var/cache/nginx/api
levels=1:2
keys_zone=api_cache:20m
max_size=1g
inactive=30m
use_temp_path=off;
server {
listen 80;
listen [::]:80;
server_name example.com;
root /var/www/example/public;
index index.html;
location = /health {
access_log off;
default_type text/plain;
return 200 "ok\n";
}
location /assets/ {
try_files $uri =404;
expires 1y;
add_header Cache-Control "public, immutable";
}
location /api/public/ {
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;
proxy_cache api_cache;
proxy_cache_valid 200 5m;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status always;
proxy_pass http://api_backend;
}
location /api/ {
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;
proxy_cache off;
proxy_pass http://api_backend;
}
location / {
try_files $uri $uri/ /index.html;
}
}После сохранения:
sudo nginx -t && sudo systemctl reload nginx
curl -I http://example.com/Базовые рекомендации
- Всегда выполняйте
nginx -tпередreload. - Не используйте права
777для решения проблем доступа. - Не публикуйте
.git,.env, резервные копии и конфигурационные файлы. - Ограничивайте размер тела запроса через
client_max_body_size. - Настраивайте разумные тайм-ауты.
- Не кешируйте персональные ответы без надёжного разделения кеша.
- Не держите уровень
debugвключённым постоянно. - Для публичного сайта используйте HTTPS.
- Проверяйте маршруты реальными запросами через
curl.
Пример ограничения загрузки:
client_max_body_size 10m;Запрет доступа к скрытым файлам, кроме .well-known:
location ~ /\.(?!well-known/) {
deny all;
}Краткая памятка
sudo systemctl enable --now nginx
sudo systemctl status nginx
sudo nginx -t
sudo nginx -T
sudo systemctl reload nginx
sudo tail -f /var/log/nginx/error.logСтатический сайт:
location / {
try_files $uri $uri/ =404;
}Reverse proxy:
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:8000;
}Главное правило: сначала nginx -t, затем systemctl reload nginx. Особенно внимательно проверяйте завершающий / в proxy_pass, сочетание root/alias и правила кеширования.