
Как HTTP-запрос проходит через Nginx
Разбираем путь HTTP-запроса через Nginx: listen, default_server, server_name, location, root, proxy_pass, коды ответа и диагностика выбранного обработчика.
Содержание
Когда один сервер обслуживает несколько сайтов, Nginx должен понять, какой из них запросил клиент и что делать с конкретным адресом страницы. Он принимает соединение на указанном порту, выбирает виртуальный сервер по имени домена, ищет подходящее правило location и только затем читает файл либо передаёт запрос приложению.
Эта последовательность помогает объяснить ситуацию, когда домен неожиданно показывает соседний сайт или существующая страница возвращает 404. Вместо перебора директив можно определить этап, на котором запрос свернул не туда.
Из чего состоит запрос
Начнём с данных, которые получает Nginx. В сокращённом HTTP-запросе ниже клиент просит страницу /docs/start, передаёт параметр source=menu и называет домен в заголовке Host:
GET /docs/start?source=menu HTTP/1.1
Host: example.com
Здесь:
- метод —
GET; - путь URI —
/docs/start; - query string —
source=menu; - имя сайта —
example.com.
DNS нужен клиенту, чтобы получить адрес, но Nginx не обращается к публичному DNS для выбора локального server_name. Он получает соединение на конкретный адрес/порт и читает имя из TLS SNI и HTTP-запроса.
Сначала Nginx принимает соединение
Директива listen связывает блок server с адресом и портом. В примере первый блок явно назначен сервером по умолчанию для порта 80, а второй предназначен для example.com:
server {
listen 80 default_server;
server_name _;
return 404;
}
server {
listen 80;
server_name example.com;
root /var/www/example;
}
default_server — свойство listen, а не особое имя. Для другого адреса или порта может быть свой сервер по умолчанию. Значение _ здесь лишь имя, которое обычно не совпадает с реальным Host; роль по умолчанию задаёт именно параметр default_server.
Для HTTPS до HTTP-заголовков выполняется TLS handshake. Клиент обычно сообщает имя через SNI, чтобы Nginx выбрал подходящий сертификат. Поэтому отладка HTTPS должна проверять и адрес, и имя, а не только порт 443.
Затем учитывается имя домена
В директиве server_name перечисляются имена, которые обслуживает блок. Для сайта с основным доменом и вариантом www запись выглядит так:
server_name example.com www.example.com;
Nginx поддерживает wildcard и регулярные выражения, но они нужны реже и имеют собственный порядок приоритета. Для небольшого набора доменов перечислите точные имена: конфигурацию проще проверить и сопровождать.
Запрос с неизвестным Host попадёт в default server. Не оставляйте там случайный первый сайт: явно определите безопасное поведение, чтобы неизвестное имя не показывало чужой проект.
Внутри сайта выбирается правило location
location связывает путь запроса с правилом обработки. Следующий блок отдельно отвечает на /health, обслуживает файлы из /assets/ и проверяет остальные пути в каталоге сайта:
location = /health {
return 200 "ok\n";
}
location /assets/ {
root /var/www/example;
}
location / {
try_files $uri $uri/ =404;
}
Точное location = /health завершает поиск для этого URI. Среди строковых префиксов выбирается самый длинный. Затем при обычных условиях проверяются регулярные locations в порядке записи, и первое совпадение может заменить сохранённый префикс.
Query string не участвует в сопоставлении. /assets/app.css?v=2 выбирает location по /assets/app.css.
Подробные правила и разница директив разобраны в уроке про location, root, alias и try_files.
Выбранное правило выполняет действие
После выбора контекста Nginx должен выполнить действие:
- прочитать статический файл;
- вернуть код или редирект через
return; - передать запрос приложению через
proxy_pass; - отправить его в FastCGI, например PHP-FPM;
- выполнить другой модульный обработчик.
Для статического сайта root задаёт базовый каталог, а try_files последовательно проверяет файл, каталог и только затем возвращает 404:
root /var/www/example;
location / {
try_files $uri $uri/ =404;
}
URI /docs/index.html будет проверен как путь под /var/www/example. Существование файла недостаточно: worker-процессу Nginx нужны права пройти все каталоги и прочитать файл.
Динамический запрос можно передать локальному приложению. Upstream в этом контексте — программа, которая формирует ответ за Nginx; ниже она слушает порт 3000 только на локальном адресе:
location /api/ {
proxy_pass http://127.0.0.1:3000;
}
Здесь дальнейший результат зависит от доступности локального приложения и правил преобразования URI. Отдельная инструкция находится в статье о reverse proxy Nginx.
Как увидеть фактическую конфигурацию
Не пытайтесь восстановить действующую конфигурацию только по одному открытому файлу: в ней могут быть include. Начните с безопасной проверки синтаксиса и доступности упомянутых файлов:
sudo nginx -t
После успешной проверки ключ -T выведет объединённое содержимое основного файла и всех подключений. Команда ничего не применяет:
sudo nginx -T
В вывод могут попасть внутренние адреса и пути. Не публикуйте его целиком. Ищите нужный listen, server_name и окружающий server-блок.
Если nginx -t завершился сообщениями syntax is ok и test is successful, перечитайте конфигурацию без остановки всего сервера, а затем проверьте состояние службы:
sudo systemctl reload nginx
systemctl status nginx --no-pager
Reload запускает workers с новой конфигурацией и корректно завершает старые. Успешный exit команды ещё не заменяет HTTP-проверку.
Проверяйте выбор без ожидания DNS
Для обычного HTTP направьте запрос на локальный адрес и передайте нужный Host. Так публичная DNS-запись не участвует в проверке:
curl -i -H 'Host: example.com' http://127.0.0.1/docs/start
При HTTPS одного заголовка недостаточно: имя используется ещё до HTTP при выборе сертификата. Параметр --resolve связывает домен с тестовым IP только для этого запуска curl:
curl -i --resolve example.com:443:127.0.0.1 https://example.com/docs/start
Проверка сертификата останется включённой. Если сертификат для имени ещё не установлен, не маскируйте проблему постоянным -k; сначала завершите корректную настройку TLS.
Добавьте временный диагностический заголовок только в тестовой среде либо верните уникальный текст из конкретного location. Так можно доказать, какой блок выбран, не угадывая по внешнему виду страницы.
Читайте код ответа как подсказку
| Код | Что проверить сначала |
|---|---|
| 404 | выбранный server/location, root, наличие файла |
| 403 | права каталогов и файла, index, политика доступа |
| 301/302 | return, trailing slash, канонический адрес |
| 502 | состояние upstream, адрес/порт/сокет, журнал ошибок |
| 504 | таймаут и фактическое время ответа upstream |
Один код не доказывает причину. Сопоставьте запрос с access.log, а серверную ошибку — с error.log и журналом приложения.
Проверьте изменение и предусмотрите возврат
Для проверки нового правила не обязательно сразу менять публичный DNS. Создайте отдельный конфигурационный файл и тестовый каталог, используйте уникальное имя только в локальном curl, выполните nginx -t, затем reload. Проверьте известный и неизвестный Host.
Для отката верните предыдущий файл конфигурации, снова выполните nginx -t и reload. Если тест синтаксиса не проходит, не применяйте изменение: работающие workers продолжают использовать прежнюю конфигурацию.
Чтобы связать этот путь с реальными файлами конфигурации, разберите контексты и include Nginx. Выбор сайта по адресу и имени проверяется через виртуальные хосты.
Первоисточники
- Как Nginx обрабатывает запрос — выбор virtual server и location.
- Модуль ngx_http_core_module — официальные правила
location,root,aliasиtry_files.
Самопроверка
Проверьте, что материал усвоен
Ответьте на все вопросы. Результат сохранится только в этом браузере и будет учтён в статистике прочитанных материалов.
Разбираем коротко
Частые вопросы
Как Nginx выбирает виртуальный сервер для запроса?
Сначала учитывается адрес и порт директивы listen, затем имя из SNI и HTTP Host на соответствующих стадиях. Если имя не совпало, запрос обрабатывает default_server для выбранной пары адреса и порта.
Учитывает ли location параметры после знака вопроса?
Сопоставление location выполняется по нормализованной части URI без аргументов запроса. Параметры доступны отдельно через переменные, но порядок key=value не должен определять выбор location.
Почему локальный curl может попадать не в тот сайт?
Запрос к 127.0.0.1 без нужного Host выбирает сервер по умолчанию. Передайте заголовок Host или используйте --resolve, чтобы проверить конкретный домен на нужном адресе без изменения публичного DNS.


