1. 1
  2. 2
  3. 3
  4. 4
  5. 5

# теория · шаг 1 из 5

Как URI превращается в путь

Коротко

Server-блок выбран, location выбран. Если внутри нет ни proxy_pass, ни return, nginx ищет файл на диске - и путь к нему строится сложением.

  • root + весь URI = путь на диске. Имя location в пути не участвует никак: оно решило, какой блок отвечает, и на этом его роль кончилась.
  • Пиши root один раз в server. Продублированный в каждом блоке, он однажды будет забыт, и сайт покажет приветственную страницу дистрибутива.
  • Каталог без index-файла даёт 403, а не 404. Это не про права.
  • Точный путь, который nginx пробовал открыть, всегда лежит в error_log, и там же написано, чего не хватило: файла или прав.

Если формула root + URI и три исхода на каталоге очевидны - листай до «Теперь сам».

Сначала ответь сам

/etc/nginx/conf.d/shop.confhttp
server {
    listen 80;
    server_name shop.local;
    root /var/www/shop;

    location /images/ {
        root /var/www/static;
    }
}

Запрос GET /images/logo.png. Какой файл откроет nginx?

Инстинктивный ответ - /var/www/static/logo.png: location же «про картинки», значит /images/ он и обозначает. Настоящий - /var/www/static/images/logo.png. Префикс из URI никуда не делся.

На что это похоже

root - это не «переход в каталог», а приставка к адресу.

Представь адрес на конверте: «Ленина, 12, кв. 5». Указание «искать в Заречном районе» не отменяет улицу и дом - оно приписывается спереди: «Заречный район, Ленина, 12, кв. 5». Никто не ждёт, что от адреса при этом отвалится улица.

Ошибка возникает от другой привычки - от cd. В командной строке «зайти в каталог» действительно меняет точку отсчёта и укорачивает путь. root работает не так: он ничего не укорачивает, он приписывает.

Механизм: сложение, буква в букву

директива root /var/www/static запрос /images/logo.png складываются целиком, без выкусывания /var/www/static/images/logo.png Имя location в пути не участвует. Оно решило, кто отвечает, и на этом его работа закончилась.
Формула ровно одна: root плюс весь URI. Каталог images внутри static обязан существовать - совпадение имён здесь по договорённости, а не автоматика.

Строка запроса отрезается заранее: /logo.png?v=3 ищется как /logo.png.

Правило

root + весь URI = путь на диске. Пиши root один раз в server и переопределяй только там, где каталог действительно другой.

Конфиг, где root продублирован в каждом блоке, ломается предсказуемо: однажды его забудут в новом location, и запросы уедут в каталог по умолчанию, где лежит приветственная страница дистрибутива. Симптом «вместо сайта показывается Welcome to nginx» почти всегда означает именно это.

Разбор: три исхода на запросе к каталогу

Если URI кончается слешем, файла с таким именем не существует по определению - это каталог. Тут вступает index. Проверено на nginx 1.31.5, в заголовок ответа выведен $request_filename - путь, который nginx собрался открыть.

/etc/nginx/conf.d/shop.confhttp server
root /www;
index index.html;
add_header X-Path "$request_filename" always;

index - какой файл отдать, если запрошен каталог; можно перечислить несколько, возьмётся первый существующий.

$  команда
for u in /about.html /docs/ /empty/ /net-takogo; do echo "== $u"; curl -sI localhost$u | grep -E 'HTTP|X-Path'; done
↳  выводfor u in /about.html /docs/ /empty/ /net-takogo; do echo "== $u"; curl -sI localhost$u | grep -E 'HTTP|X-Path'; done
== /about.html
HTTP/1.1 200 OK
X-Path: /www/about.html
== /docs/
HTTP/1.1 200 OK
X-Path: /www/docs/index.html
== /empty/
HTTP/1.1 403 Forbidden
X-Path: /www/empty/
== /net-takogo
HTTP/1.1 404 Not Found
X-Path: /www/net-takogo

grep -E 'HTTP|X-Path' оставляет из заголовков строку статуса и X-Path: -E разрешает в шаблоне |, «или».

Третий запрос - источник постоянной путаницы. Каталог empty существует, но index-файла в нём нет, а листинг по умолчанию запрещён. Человек видит 403 и идёт чинить права доступа, хотя nginx сообщает совсем другое: показывать нечего.

Что на диске Директивы Ответ
docs/index.html есть любые 200, файл
каталога нет вовсе любые 404
каталог есть, index-файла нет autoindex off (по умолчанию) 403
каталог есть, index-файла нет autoindex on 200, листинг файлов

autoindex on - это публикация каталога

Листинг покажет всё, что лежит в каталоге: забытые дампы, .bak, архивы с исходниками. Включать его стоит осознанно и точечно - на конкретном location с файлами, которые и должны быть публичными: location /files/ { autoindex on; }.

Разбор: где наивное правило подводит

Наивное правило: «403 - значит, права на файл не те».

Прав может не хватать, но проверять это надо не догадкой. error_log пишет полный путь и причину. Два запроса на одном сервере:

↳  выводtail -2 /var/log/nginx/error.log
2026/09/10 19:49:50 [error] 23#23: *4 open() "/www/net-takogo" failed (2: No such file or directory), client: 172.17.0.1, server: , request: "GET /net-takogo HTTP/1.1", host: "localhost"
2026/09/10 19:49:50 [error] 20#20: *5 open() "/www/zakryto/file.txt" failed (13: Permission denied), client: 172.17.0.1, server: , request: "GET /zakryto/file.txt HTTP/1.1", host: "localhost"

Это два разных разговора. Вторая ошибка (13: Permission denied) действительно про права (r - читать, x у каталога - пройти насквозь, режим 700 - всё владельцу и ничего остальным; буквы разбирались в уроке установки), и вот там есть подвох: воркеру мало права r на сам файл. Ему нужно право x на все каталоги по пути - /var, /var/www, /var/www/shop, /var/www/shop/images. Классическая засада - файлы разложили в домашний каталог /home/user/site, а /home/user закрыт режимом 700.

Проверяется это одной командой:

$  команда
sudo -u www-data namei -l /var/www/shop/images/logo.png

sudo -u www-data запускает команду от имени пользователя, под которым работают воркеры (в официальном образе Docker он называется nginx), а namei -l печатает права на каждом уровне пути и сразу показывает, где обрыв.

Что ломается без этого

  • «Вместо сайта показывается Welcome to nginx». В этом location забыли root, и он взялся из настроек по умолчанию.
  • 403 на каталоге. Нет index-файла, а листинг выключен. Это не про права.
  • 404 при том, что файл точно на месте. Смотри error_log: скорее всего, nginx искал его по другому пути - root сложился не с тем URI, который ты держишь в голове.
  • 403 при верных правах на файл. Проверь права на прохождение всех каталогов пути.

Зачем это в работе

Разбор «файл не отдаётся» почти всегда начинается с одного вопроса: какой путь nginx попробовал открыть на самом деле. Пока на него нет ответа, всё остальное - гадание.

Ответ достаётся двумя способами, и оба занимают меньше минуты. На своём сервере - tail -f /var/log/nginx/error.log и один запрос: в строке будет и путь, и причина, и какой server-блок отвечал. Там, где в лог не заглянуть, - временный заголовок с $request_filename, который печатает тот же путь прямо в ответ.

Убирать такой заголовок перед выкладкой обязательно: раскладка файловой системы наружу не нужна никому, кроме того, кто ищет, что у тебя ещё лежит рядом.

Теперь сам

1. root /var/www; в server и location /static/ { } без своего root. Где будет искаться /static/app.css?

/var/www/static/app.css. root наследуется, URI приклеивается целиком. Каталог static внутри /var/www обязан существовать.

2. Хочется отдавать /downloads/ из /mnt/big-disk, причём на диске файлы лежат прямо в корне, без подкаталога downloads. Сработает ли location /downloads/ { root /mnt/big-disk; }?

Нет: получится /mnt/big-disk/downloads/..., а такого каталога нет. Либо завести его на диске, либо взять alias - о нём следующий урок.

3. Запрос /docs/ вернул 403. Каталог существует, права 755. Что проверить первым?

Наличие index-файла. При autoindex off каталог без него и даёт 403 - и это самая частая причина. Права проверяются вторыми, командой namei -l от имени пользователя, под которым работают воркеры.

Главное

root не заменяет путь, а складывает: root + весь URI = файл на диске, и имя location в пути не участвует. Каталог без index-файла даёт 403, а не 404, а точный путь и причину отказа всегда пишет error_log.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.