БАЗА ЗНАНИЙ
Nginx: что это, как работает и как настроить на сервере
Nginx — что это простыми словами: как работает веб-сервер, чем отличается от Apache, как установить его на Ubuntu, настроить сайт и HTTPS и разобрать ошибки в логах.

Nginx — что это такое: веб-сервер, который принимает запросы браузеров, отдаёт файлы сайта и передаёт остальное приложению. На нём работает значительная часть сайтов в интернете, включая самые нагруженные.
Что такое Nginx простыми словами
Nginx — это программа, которая слушает сетевой порт, принимает запросы и решает, чем на них ответить. Картинку или страницу она отдаёт сама из файлов на диске, а запрос к динамической части сайта передаёт приложению: PHP, Node.js, Python.

Без веб-сервера сайт в интернете не работает: браузер просто не с кем разговаривать. Приложение на PHP само по себе тоже не слушает порт — это делает за него Nginx.
Для чего Nginx нужен на практике:
- отдавать файлы сайта: html, стили, скрипты, картинки, шрифты;
- принимать HTTPS-соединения и расшифровывать их;
- передавать запросы приложению и возвращать его ответ клиенту;
- распределять нагрузку между несколькими серверами приложения;
- кешировать ответы, сжимать их и ограничивать частоту запросов.
Название читается «энджинкс», от английского engine x. Первая версия вышла в 2004 году, её написал Игорь Сысоев, чтобы решить проблему десяти тысяч одновременных соединений на одном сервере.
Как работает Nginx
Разница с классическими веб-серверами в том, как обрабатываются соединения. Старый подход: на каждого клиента свой процесс или поток. Пока клиент качает страницу по медленному мобильному интернету, процесс занят и держит память.

Nginx устроен иначе. Он запускает несколько рабочих процессов, обычно по числу ядер, и каждый держит тысячи соединений одновременно. Процесс не ждёт медленного клиента, а переключается на те соединения, где уже есть данные для обработки.
Число рабочих процессов и лимит соединений на каждый задаются в главном файле. По умолчанию процессов столько же, сколько ядер у сервера, а соединений на процесс — 768 или 1024 в зависимости от сборки. Перемножив эти два числа, вы получите потолок одновременных подключений, который сервер способен держать.
Одно соединение при этом не равно одному запросу. Браузер держит соединение открытым и успевает вытянуть через него десяток файлов — это называется keepalive. Поэтому тысяча одновременных соединений обслуживает заметно больше тысячи посетителей в минуту.
Поэтому на том же железе Nginx принимает кратно больше одновременных подключений и расходует память предсказуемо. Для сайта на небольшом VPS это значит, что всплеск посещаемости упрётся в приложение и базу данных раньше, чем в веб-сервер.
Nginx или Apache
Apache — второй распространённый веб-сервер, и выбор между ними возникает постоянно.
| Параметр | Nginx | Apache |
|---|---|---|
| Модель обработки | событийная, один процесс на тысячи соединений | процесс или поток на соединение |
| Статические файлы | отдаёт быстрее и экономнее | медленнее на том же железе |
| Настройки в каталогах сайта | нет, только общий конфиг | есть, файлы .htaccess |
| Модули | подключаются при сборке | подключаются на лету |
| Где силён | статика, HTTPS, прокси, высокая нагрузка | готовые CMS, legacy-проекты, гибкость правил |
На практике их часто ставят вместе: Nginx принимает соединения снаружи, отдаёт статику и расшифровывает HTTPS, а динамику передаёт Apache.

Для нового проекта на VPS обычно хватает одного Nginx: приложение подключается к нему напрямую, без второго веб-сервера в цепочке.
Где Nginx уже стоит, а где его ставить не нужно
Прежде чем ставить веб-сервер, проверьте, не работает ли он уже.

На виртуальном хостинге веб-сервер уже настроен провайдером и обслуживает сразу много клиентов. Поставить туда свой Nginx нельзя, да и незачем: настройки меняются через панель управления, а часть правил — через .htaccess, если на сервере работает Apache.
На VPS сервер достаётся пустым. Здесь Nginx ставите вы, конфигурацию пишете сами и сами же следите за обновлениями. Взамен получаете полный контроль: любые правила, любые модули, своя схема кеширования.
Установка на Ubuntu и Debian
Подключитесь к серверу по SSH и поставьте пакет из репозитория дистрибутива.
sudo apt update
sudo apt install nginx
После установки служба запускается сама и добавляется в автозагрузку. Проверить это можно двумя командами: одна показывает состояние службы, вторая — что сервер отвечает по сети.

Если curl возвращает 200, а в браузере по адресу сервера открывается страница «Welcome to nginx» — веб-сервер работает. Когда страница не открывается, а служба активна, дело обычно в файрволе: порт 80 закрыт.
sudo ufw allow 'Nginx Full'
Версия из репозитория Ubuntu или Debian отстаёт от свежей на несколько месяцев. Для большинства сайтов это нормально: обновления безопасности в неё приходят. Если нужна последняя версия, подключают официальный репозиторий nginx.org.
Структура конфигурации
Все настройки лежат в /etc/nginx. Разобраться в них проще, если представлять каталог как дерево.

| Путь | Что внутри |
|---|---|
/etc/nginx/nginx.conf |
главный файл: число процессов, логи, общие настройки, подключение остальных файлов |
/etc/nginx/sites-available/ |
конфигурации всех сайтов, по файлу на сайт |
/etc/nginx/sites-enabled/ |
символические ссылки на включённые сайты |
/etc/nginx/conf.d/ |
общие куски настроек, подключаются ко всем сайтам |
/var/log/nginx/ |
access.log с запросами и error.log с ошибками |
/var/www/ |
файлы сайтов |
Разделение на sites-available и sites-enabled нужно, чтобы выключать сайт, не удаляя его конфигурацию: достаточно убрать ссылку. Такая схема есть в сборках Debian и Ubuntu, в других дистрибутивах конфигурации сайтов кладут прямо в conf.d.
Настройки живут во вложенных контекстах. Самый внешний — http, внутри него блоки server для каждого сайта, внутри них блоки location для отдельных путей. Директива, заданная выше, действует во всех вложенных блоках, пока её не переопределят. Поэтому сжатие или таймауты обычно задают один раз в http, а не копируют в каждый сайт.
С логами это правило даёт неожиданный эффект. Если access_log указан и в http, и в блоке сайта, запись пойдёт только в тот файл, что указан ближе к location, а не в оба сразу.
Синтаксис везде одинаковый: директива и значение заканчиваются точкой с запятой, блоки заключаются в фигурные скобки, вложенность имеет значение. Директива из внешнего блока действует во внутренних, пока её не переопределят.
Первый сайт: блок server
Один сайт — один блок server. Минимальный рабочий конфиг выглядит так.

Что делает каждая строка:
listen 80— слушать восьмидесятый порт, то есть обычный HTTP;server_name— для каких доменов работает этот блок;root— каталог, в котором лежат файлы сайта;index— какой файл отдавать, если запрошен каталог;try_files $uri $uri/ =404— искать файл, затем каталог, иначе вернуть 404.
Файл сохраняют в sites-available, затем включают сайт ссылкой и применяют настройки.
sudo ln -s /etc/nginx/sites-available/example.com.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Если на сервере несколько сайтов, Nginx выбирает блок по заголовку Host из запроса. Когда ни один server_name не подошёл, отвечает первый блок или тот, что помечен как default_server. Поэтому заводят отдельный блок-заглушку, который отдаёт 444 на чужие домены, направленные на ваш адрес.
Директива location
Внутри блока server правила для отдельных путей задаются через location. Разные варианты записи дают разный приоритет, и это чаще всего путает.

Порядок такой: сначала проверяется точное совпадение со знаком =, затем самый длинный префикс, затем регулярные выражения по порядку в файле. Префикс с ^~ отменяет проверку регулярных выражений, если он подошёл.
location = /favicon.ico { access_log off; expires 30d; }
location ^~ /static/ {
root /var/www/example.com;
expires 7d;
}
location ~* \.(jpg|png|webp|css|js)$ {
expires 30d;
add_header Cache-Control "public";
}
Директива try_files внутри location определяет, что делать с запросом дальше: искать файл, потом каталог, потом передать запрос приложению. Для сайта на CMS последний аргумент обычно указывает на точку входа, например /index.php?$args — так работают «красивые» адреса без имени файла в URL.
Частая ошибка — положить общий location / выше остальных и ждать, что он перехватит всё. Порядок в файле для префиксов не важен, важна длина совпадения. А вот для регулярных выражений порядок как раз решает: сработает первое подходящее.
Nginx перед приложением: proxy_pass
Сайт на WordPress, Битриксе или Node.js работает как программа, а не как набор файлов. Nginx принимает запрос и передаёт его этой программе, а ответ возвращает браузеру.

Для приложения на Node.js, Python или в контейнере используют proxy_pass.
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
Заголовки здесь не формальность. Без них приложение увидит все запросы как пришедшие с локального адреса и не узнает ни домен, ни настоящий IP посетителя, ни то, что соединение было защищённым.
У proxy_pass есть ловушка со слешем на конце. Запись proxy_pass http://127.0.0.1:3000; передаёт приложению полный путь запроса, а proxy_pass http://127.0.0.1:3000/; отрезает префикс из location. Из-за этой одной черты запросы уходят не туда, а в логах приложения появляются 404 на существующие страницы.
Когда приложение живёт не на одном порту, а на нескольких, адреса выносят в блок upstream, и Nginx начинает распределять запросы между ними. Это и есть балансировка нагрузки.
upstream app {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
keepalive 32;
}
location / {
proxy_pass http://app;
proxy_set_header Host $host;
}
По умолчанию запросы раздаются по кругу. Если приложение хранит сессию в памяти процесса, такой режим ломает авторизацию: следующий запрос того же человека уйдёт на другой экземпляр. Лечится либо общим хранилищем сессий, либо привязкой клиента к серверу по его адресу.
PHP подключается иначе, через FastCGI и сокет PHP-FPM.
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
Путь к сокету зависит от версии PHP, его видно в настройках пула PHP-FPM. Если ошибиться в версии, Nginx вернёт посетителю 502, а в error.log появится запись о том, что соединение к upstream не установлено.
HTTPS на Nginx
Сертификат подключается двумя директивами в блоке, который слушает порт 443. Как получить сам сертификат, разобрано в статье про SSL-сертификаты.

server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
root /var/www/example.com;
index index.html;
}
В ssl_certificate указывают файл с полной цепочкой, а не только сертификат сайта: иначе мобильные приложения и часть клиентов получат ошибку проверки. При выпуске через certbot это файл fullchain.pem.
Строка http2 on; включает вторую версию протокола: браузер тянет файлы по одному соединению параллельно, и страница с десятками картинок собирается заметно быстрее. В версиях Nginx старше 1.25 то же самое записывалось как listen 443 ssl http2;.
Отдельно решается вопрос с www. Поисковики считают example.com и www.example.com разными адресами, поэтому один из них делают основным, а со второго ставят постоянный редирект — тем же блоком с return 301.
Редирект с HTTP лучше делать отдельным блоком, как в примере. Вариант с проверкой внутри общего блока работает, но читается хуже и чаще ломается при правках.
Nginx в Docker
В контейнере Nginx запускают, когда сайт собирается образом или когда на сервере несколько независимых проектов.

Файлы сайта и конфигурацию монтируют с хоста, чтобы не пересобирать образ при каждой правке. Флаг :ro подключает их только на чтение — контейнеру писать туда незачем.
Проверять конфигурацию нужно внутри контейнера: на хосте может стоять другая версия Nginx с другими модулями. Перечитать конфигурацию без остановки контейнера можно командой docker exec web nginx -s reload.
Команды и проверка конфигурации
Главная привычка при работе с Nginx: проверять конфигурацию до того, как применять её.

Команда nginx -t разбирает все файлы и либо подтверждает, что синтаксис в порядке, либо указывает файл и строку с ошибкой. Пока проверка не пройдена, перезагружать сервер нельзя: при ошибке в конфиге reload оставит старые настройки, а restart остановит сайт совсем.

Разница между reload и restart важнее, чем кажется. Первая команда поднимает новые рабочие процессы с новой конфигурацией, а старые дорабатывают текущие запросы и завершаются — посетители ничего не замечают. Вторая останавливает сервер целиком, и на долю секунды сайт недоступен.
Логи и типичные ошибки
Nginx пишет два журнала: access.log с запросами и error.log с проблемами. Второй отвечает почти на все вопросы вида «сайт не работает, а почему — непонятно».

| Что в логе или в браузере | Причина | Что делать |
|---|---|---|
502 Bad Gateway, connect() failed |
приложение за Nginx не запущено или указан не тот сокет | проверить службу приложения и путь в fastcgi_pass или proxy_pass |
| 504 Gateway Time-out | приложение отвечает дольше таймаута | искать причину в приложении, при необходимости поднять proxy_read_timeout |
403 Forbidden, Permission denied |
у процесса нет прав на файлы сайта | проверить владельца каталога и права |
| 404 на существующий файл | неверный root или лишний сегмент пути |
сверить root и реальный путь к файлу |
bind() to 0.0.0.0:80 failed |
порт занят другим веб-сервером | остановить Apache или сменить порт |
| Сайт отдаёт чужой контент | не совпал server_name, ответил блок по умолчанию |
проверить домены в блоках и default_server |
Формат записей в access.log задаётся директивой log_format. В стандартный вариант входят адрес клиента, время, строка запроса, код ответа, размер ответа и заголовки Referer и User-Agent. Если сайт работает за внешним балансировщиком или CDN, в логе будет адрес этого посредника, а настоящий адрес посетителя приедет в заголовке X-Forwarded-For — его стоит добавить в формат, иначе разбирать атаки и всплески трафика будет не по чему.
Полезно добавить в формат и время обработки запроса, переменную $request_time. С ней видно не только то, что страница отдалась, но и за сколько: медленные запросы к приложению вылезают сразу.
Смотреть лог удобнее вживую, оставив команду в соседнем окне терминала, и повторять проблемный запрос.
sudo tail -f /var/log/nginx/error.log
В access.log полезно смотреть распределение кодов ответов: если доля 5xx заметно выросла, проблема на стороне сервера, а не у отдельного посетителя.
Что настроить после запуска
Сайт открылся, дальше короткий список настроек.

Сжатие и кеширование статики заметно ускоряют загрузку и настраиваются в несколько строк.
gzip on;
gzip_types text/css application/javascript application/json image/svg+xml;
gzip_min_length 1024;
Лимит частоты запросов защищает тяжёлые места сайта от перебора и сбивает грубые DDoS-атаки на уровне приложения.
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
location /wp-login.php {
limit_req zone=login burst=3 nodelay;
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
Предельный размер тела запроса вспоминают обычно в неподходящий момент. По умолчанию Nginx принимает не больше мегабайта, и попытка загрузить через админку картинку потяжелее заканчивается ошибкой 413. Лимит поднимают директивой client_max_body_size, обычно в блоке сайта.
Таймауты задайте явно, иначе медленные клиенты будут держать соединения бесконечно. И проверьте, что ротация логов включена: на нагруженном сайте access.log растёт быстро и может занять весь диск.
Частые вопросы
proxy_pass.nginx -t перед каждым reload: команда покажет файл и строку с ошибкой.systemctl reload nginx. Она поднимает процессы с новой конфигурацией, а старые дорабатывают текущие запросы./var/log/nginx: запросы в access.log, ошибки в error.log. Пути можно переопределить в конфигурации для каждого сайта отдельно.sites-available на сайт. Сайт выключается удалением ссылки из sites-enabled, конфигурация при этом сохраняется.nginx -V выводит версию и полный список параметров сборки — по нему видно, какие модули доступны.

