ВЕРНУТЬСЯ

БАЗА ЗНАНИЙ

Nginx: что это, как работает и как настроить на сервере

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

Nginx: что это, как работает и как настроить на сервере

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.

Схема: Nginx принимает клиентов, отдаёт статику и передаёт динамические запросы Apache через proxy_pass
Связка, в которой каждый делает то, что умеет лучше

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

Где Nginx уже стоит, а где его ставить не нужно

Прежде чем ставить веб-сервер, проверьте, не работает ли он уже.

Сравнение: на виртуальном хостинге веб-сервер настроен провайдером, на VPS его ставит и настраивает владелец
Граница ответственности на хостинге и на своём сервере

На виртуальном хостинге веб-сервер уже настроен провайдером и обслуживает сразу много клиентов. Поставить туда свой Nginx нельзя, да и незачем: настройки меняются через панель управления, а часть правил — через .htaccess, если на сервере работает Apache.

На VPS сервер достаётся пустым. Здесь Nginx ставите вы, конфигурацию пишете сами и сами же следите за обновлениями. Взамен получаете полный контроль: любые правила, любые модули, своя схема кеширования.

Установка на Ubuntu и Debian

Подключитесь к серверу по SSH и поставьте пакет из репозитория дистрибутива.

sudo apt update
sudo apt install nginx

После установки служба запускается сама и добавляется в автозагрузку. Проверить это можно двумя командами: одна показывает состояние службы, вторая — что сервер отвечает по сети.

Терминал: установка nginx через apt, вывод systemctl status с состоянием active (running) и ответ curl с кодом 200
Формат вывода команд после установки

Если curl возвращает 200, а в браузере по адресу сервера открывается страница «Welcome to nginx» — веб-сервер работает. Когда страница не открывается, а служба активна, дело обычно в файрволе: порт 80 закрыт.

sudo ufw allow 'Nginx Full'

Версия из репозитория Ubuntu или Debian отстаёт от свежей на несколько месяцев. Для большинства сайтов это нормально: обновления безопасности в неё приходят. Если нужна последняя версия, подключают официальный репозиторий nginx.org.

СЕРВЕР ПОД ВАШИ ЗАДАЧИ БЫСТРО И БЕЗ ЛИШНИХ СЛОЖНОСТЕЙ

Подберите конфигурацию, оплатите и начните работу за несколько минут — без сложной настройки

Структура конфигурации

Все настройки лежат в /etc/nginx. Разобраться в них проще, если представлять каталог как дерево.

Дерево каталогов: /etc/nginx с nginx.conf, sites-available, sites-enabled и conf.d, каталоги логов и файлов сайтов
Где что лежит после установки из пакета
Путь Что внутри
/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. Минимальный рабочий конфиг выглядит так.

Карточка конфигурации: блок server с директивами listen, server_name, root, index и location
Минимальная конфигурация сайта со статическими файлами

Что делает каждая строка:

  • 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: точное совпадение, префикс без проверки регулярных выражений, регулярное выражение и обычный префикс
Как Nginx выбирает, какой 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 принимает запрос и передаёт его этой программе, а ответ возвращает браузеру.

Схема обратного прокси: Nginx отдаёт статику сам, а запросы к приложению передаёт в PHP-FPM или на localhost:3000
Статику отдаёт 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-сертификаты.

Схема: порт 80 только перенаправляет на 443, где подключены сертификат и ключ
Обычная схема: весь трафик уходит на защищённый порт
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 запускают, когда сайт собирается образом или когда на сервере несколько независимых проектов.

Терминал: запуск контейнера nginx с пробросом порта и монтированием каталогов, проверка конфигурации внутри контейнера
Запуск контейнера с внешними конфигом и файлами сайта

Файлы сайта и конфигурацию монтируют с хоста, чтобы не пересобирать образ при каждой правке. Флаг :ro подключает их только на чтение — контейнеру писать туда незачем.

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

Команды и проверка конфигурации

Главная привычка при работе с Nginx: проверять конфигурацию до того, как применять её.

Терминал: команда nginx -t показывает успешную проверку, а при опечатке в директиве выводит номер строки и файл с ошибкой
Проверка находит опечатку и показывает файл со строкой

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

Шпаргалка команд: nginx -t, systemctl reload, restart, status, nginx -V, просмотр error.log
Команды, которые нужны каждый день

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

Логи и типичные ошибки

Nginx пишет два журнала: access.log с запросами и error.log с проблемами. Второй отвечает почти на все вопросы вида «сайт не работает, а почему — непонятно».

Фрагмент error.log: connect() failed, Connection refused при обращении к сокету PHP-FPM
Запись в логе говорит, что приложение за Nginx не ответило
Что в логе или в браузере Причина Что делать
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 растёт быстро и может занять весь диск.

Частые вопросы

Нет. Веб-сервер там уже настроен провайдером, и доступа к его конфигурации у вас нет. Nginx понадобится, когда переедете на VPS или выделенный сервер.
Моделью обработки соединений: Nginx держит тысячи клиентов несколькими процессами, Apache выделяет процесс или поток на соединение. Отсюда разница в расходе памяти под нагрузкой.
Да, это распространённая схема. Nginx принимает соединения, отдаёт статику и расшифровывает HTTPS, а динамические запросы передаёт Apache через proxy_pass.
Чаще всего конфигурацию применили без проверки. Запускайте nginx -t перед каждым reload: команда покажет файл и строку с ошибкой.
Nginx не смог получить ответ от приложения. Проверьте, запущен ли PHP-FPM или ваш сервис, и совпадает ли путь к сокету или порт в конфигурации.
Командой systemctl reload nginx. Она поднимает процессы с новой конфигурацией, а старые дорабатывают текущие запросы.
В каталоге /var/log/nginx: запросы в access.log, ошибки в error.log. Пути можно переопределить в конфигурации для каждого сайта отдельно.
Для раздачи статики счёт идёт на тысячи одновременных соединений даже на скромной конфигурации. Упираются обычно не в Nginx, а в приложение и базу данных за ним.
Так удобнее: один файл в sites-available на сайт. Сайт выключается удалением ссылки из sites-enabled, конфигурация при этом сохраняется.
Команда nginx -V выводит версию и полный список параметров сборки — по нему видно, какие модули доступны.