Кто ответит на имя
Первая команда — ответ глазами клиента, вторая — какие listen и
server_name nginx на самом деле прочитал, в порядке файлов.
nginx отдаёт сайты с диска и пересылает запросы приложениям; сайт выбирает по Host, конфиг проверяет nginx -t.
sudo nginx -t
проверить конфиг (только с sudo)
sudo systemctl reload nginx
применить без обрыва соединений
sudo nginx -T | grep -n server_name
какие сайты видит nginx
ls -l /etc/nginx/sites-enabled/
включённые сайты
curl -i -H 'Host: docs.lab.test' http://127.0.0.1/
кто ответит
sudo tail -n 5 /var/log/nginx/error.log
почему 404, 403, 502
sudo tail -n 5 /var/log/nginx/access.log
что спрашивали и код
sudo ss -tlnp | grep nginx
на каких портах слушает
nginx — веб-сервер: отдаёт файлы сайта с диска и работает
обратным прокси — пересылает запрос приложению и
возвращает его ответ клиенту. Служба nginx под systemd: главный процесс
от root читает конфиг, рабочие процессы от www-data обслуживают запросы. Всё,
что nginx открывает на диске или в сокете, проверяется правами www-data.
Каждый сайт — блок server. Запрос сначала попадает к блокам, которые слушают
его адрес и порт (listen), потом nginx сравнивает заголовок Host
с server_name этих блоков.
Совпало имя — отвечает этот блок. Не совпало ни одно — отвечает
сервер по умолчанию: блок с listen 80 default_server,
а без такой пометки — первый блок для этого порта. В Ubuntu сервер по умолчанию
на 80 — сайт default со страницей «Welcome to nginx!». Поэтому опечатка в
имени или забытый сайт дают не ошибку, а чужую страницу.
Файл nginx ищет так: root + путь из запроса. При
root /var/www/docs запрос /guide/a.html — это файл
/var/www/docs/guide/a.html. На запрос каталога (/) отдаётся файл из списка
index (по умолчанию index.html).
Блок location задаёт правила для части путей. Из
префиксов, с которых начинается путь, выигрывает самый длинный: location /api/
перебивает location / для /api/users.
| Директива | Путь для запроса /files/x.txt |
|---|---|
location /files/ { root /srv/data; } |
/srv/data/files/x.txt |
location /files/ { alias /srv/data/; } |
/srv/data/x.txt |
root дописывает путь целиком, alias заменяет совпавший префикс.
nginx открывает соединение к приложению — бэкенду — и
пересылает запрос. Не дозвонился — клиент получает 502 Bad Gateway, не
дождался ответа — 504 Gateway Time-out. Это ответы самого nginx: в журнал
приложения такие запросы не попадают. Бэкенд может слушать и
unix-сокет: proxy_pass http://unix:/run/app/app.sock;.
Раскладка Debian и Ubuntu:
/etc/nginx/nginx.conf — общие настройки, внизу include сайтов;/etc/nginx/sites-available/ — файлы сайтов;/etc/nginx/sites-enabled/ — ссылки на включённые сайты; nginx читает только
их;/var/log/nginx/access.log и error.log — журналы.Файл в sites-available без ссылки в sites-enabled nginx не видит. Выключить
сайт — удалить ссылку, файл остаётся.
nginx -t проверяет весь конфиг и называет файл и строку ошибки. reload
перечитывает конфиг без обрыва соединений; с ошибкой в конфиге nginx оставляет
старый. Команда systemctl reload возвращается сразу, а новые рабочие процессы
берут соединения чуть позже: curl в той же строке может увидеть старое.
access.log — строка на каждый запрос, error.log — почему запрос не удался
и что сломалось при старте. Формат access.log по умолчанию — combined:
По полям: $remote_addr (адрес клиента), -, $remote_user,
[$time_local], "$request" (метод, путь, версия), $status (код),
$body_bytes_sent (размер тела), "$http_referer", "$http_user_agent" — в
примере user-agent "curl/8.5.0" не поместился. Свой формат задаёт
log_format — в файле сайта вне блока server:
$request_time — сколько секунд nginx обрабатывал запрос. Файлы с .1 и
.2.gz рядом с журналом — прошлые, их по расписанию отодвигает и сжимает
logrotate (/etc/logrotate.d/nginx).
Есть ли URI после адреса в proxy_pass, решает, какой путь получит
приложение:
location /api/ и proxy_pass |
Запрос /api/x уходит как |
|---|---|
http://127.0.0.1:3003 |
/api/x — путь без изменений |
http://127.0.0.1:3003/ |
/x — /api/ заменено на / |
По умолчанию приложение видит Host: 127.0.0.1:3003 — адрес из proxy_pass.
Имя сайта и адрес клиента передают заголовками:
ssl_certificate — файл цепочки: сначала
сертификат сайта, за ним промежуточный. $request_uri — путь вместе с
запросом после ?: http://shop.lab.test/x?y=1 уводит на
https://shop.lab.test/x?y=1. Сертификаты и их проверка — openssl-tls.
| Директива | Что делает | Пример |
|---|---|---|
listen server |
Адрес и порт блока; default_server делает его сервером по умолчанию, ssl включает TLS. | listen 443 ssl; |
server_name server |
Имена сайта, с которыми nginx сравнивает заголовок Host. | server_name docs.lab.test www.docs.lab.test; |
root server, location |
Каталог сайта; путь файла = root + путь из запроса. | root /var/www/docs; |
index server, location |
Какой файл отдать на запрос каталога. | index index.html; |
location server |
Правила для путей с этим префиксом; самый длинный совпавший префикс выигрывает. | location /api/ { … } |
alias location |
Каталог вместо совпавшего префикса location (root дописывает путь целиком). | alias /srv/data/; |
try_files server, location |
Проверить файлы по очереди и отдать первый существующий, иначе последний вариант. | try_files $uri $uri/ =404; |
proxy_pass location |
Переслать запрос бэкенду; URI после адреса заменяет совпавший префикс location. | proxy_pass http://unix:/run/app/app.sock; |
proxy_set_header server, location |
Заголовок запроса к бэкенду; без него Host — адрес из proxy_pass. | proxy_set_header Host $host; |
proxy_read_timeout server, location |
Сколько ждать ответа бэкенда, потом 504. | proxy_read_timeout 30s; |
ssl_certificate server |
Файл сертификата; с промежуточным CA — сертификат сайта первым, за ним промежуточный. | ssl_certificate /etc/nginx/tls/fullchain.pem; |
ssl_certificate_key server |
Закрытый ключ к первому сертификату файла ssl_certificate. | ssl_certificate_key /etc/nginx/tls/site.key; |
return server, location |
Сразу ответить кодом; 301 с адресом — перенаправление. | return 301 https://$host$request_uri; |
client_max_body_size http, server, location |
Наибольшее тело запроса; больше — 413. | client_max_body_size 20m; |
error_log http, server |
Куда писать ошибки; уровень warn, error, crit и выше. | error_log /var/log/nginx/docs.error.log; |
access_log http, server, location |
Куда и в каком формате писать строку на каждый запрос. | access_log /var/log/nginx/app.log timed; |
log_format http |
Свой формат журнала из переменных — $status, $request_time и других. | log_format timed '$remote_addr "$request" $status $request_time'; |
Ничего не найдено. Попробуй запрос короче.
Первая команда — ответ глазами клиента, вторая — какие listen и
server_name nginx на самом деле прочитал, в порядке файлов.
Строка error.log называет upstream — куда nginx шёл. Приложение напрямую
отвечает — дело в proxy_pass или правах; не отвечает — в приложении.
Host запроса не совпал ни с одним server_name — ответил сервер по умолчанию.
Проверь имя в server_name и что ссылка на сайт есть в sites-enabled:
sudo nginx -T | grep server_name.
В error.log — путь, который собрал nginx:
open() "/var/www/portal/nope.html" failed (2: No such file or directory).
Сравни его с настоящим путём файла: обычно ошибка в root или alias.
(13: Permission denied) — www-data не может прочитать файл или войти в
каталог по пути; права по всему пути покажет namei -l.
directory index of "…" is forbidden — в каталоге нет файла из index.
nginx не открыл соединение с бэкендом. Причину называет строка error.log:
| В error.log | Что значит |
|---|---|
(111: Connection refused) |
никто не слушает адрес или сокет |
(13: Permission denied) |
у www-data нет права на сокет |
(2: No such file or directory) |
нет файла сокета по этому пути |
Полностью: connect() failed (111: Connection refused) while connecting to
upstream, … upstream: "http://127.0.0.1:3001/". Для сокета —
connect() to unix:/run/app/app.sock failed (13: Permission denied). Ошибки
13 и 2 nginx пишет с уровнем [crit], 111 — [error]: поиск только по
[error] их пропустит. Сокет остался после остановки приложения — тоже 111.
Права и пути сокетов — в статье unix-sockets.
Соединение есть, ответа нет: upstream timed out (110: Connection timed out)
while reading response header from upstream. Приложение зависло или
отвечает дольше proxy_read_timeout (по умолчанию 60 с).
unexpected "}" in …:5 — в строке перед } нет ;;a duplicate default server for 0.0.0.0:80 — default_server стоит в двух
блоках одного порта;open() "/run/nginx.pid" failed (13: Permission denied) — nginx -t
запущен без sudo.[warn] conflicting server name "docs.lab.test" on 0.0.0.0:80, ignored —
одно имя стоит в двух блоках одного порта. Проверку это не роняет, но запросы
с этим именем получает блок, загруженный первым, а второй его не получает
никогда. Найти оба:
grep -r по каталогу здесь не годится: ссылки в sites-enabled он
пропускает.
SSL_CTX_use_PrivateKey(…) failed (… key values mismatch) — первый
сертификат в файле ssl_certificate не пара ключу. Либо ключ чужой, либо в
файле цепочки первым стоит промежуточный сертификат. Сертификат сайта — первым.
nginx читает сертификаты при старте и reload. Заменил файл — sudo nginx -t
&& sudo systemctl reload nginx, проверяй через секунду.
sudo ss -tlnp | grep ':80 ' покажет, кто держит порт. Останови его или смени
listen.
Смена адреса на том же порту (listen 80 → listen 127.0.0.1:80) через
reload не проходит: systemctl reload возвращает 0, nginx остаётся на
старых сокетах, а в error.log — bind() to 127.0.0.1:80 failed (98: Address
already in use). Такую правку применяет только sudo systemctl restart nginx;
что слушается на самом деле — sudo ss -tlnp | grep nginx.
reload при ошибке оставляет работать старый конфиг, restart — останавливает
nginx и не может запустить: все сайты на машине лежат. Перед любым применением —
sudo nginx -t.
reload, restart, status.Есть неточность? Сообщить об ошибке в статье