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

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

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

Коротко

Для обычной отдачи файла root задаёт корень, к которому nginx добавляет URI. Выбранный location определяет настройку, но его имя отдельно к пути не прибавляется.

  • При root /var/www запрос /img/a.png ищет /var/www/img/a.png.
  • Вложенный root меняет начало пути, сохраняя весь URI.
  • Для существующего каталога проверяется index; без index и при выключенном autoindex nginx вернёт 403.
  • Если файла нет, проверь путь в error_log. При log_not_found off сообщения об отсутствии файлов подавляются.

Если формула 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, и запросы уедут в каталог по умолчанию, где лежит приветственная страница дистрибутива. Приветственная страница также может означать, что выбран другой server. Проверь выбранный блок и получившийся путь, прежде чем менять root.

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

Если 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 путь к файлу получается добавлением всего URI к корню. Сначала вычисли этот путь, затем сверь его с диском и error_log. Существующий каталог без index при выключенном листинге даёт 403; исправление прав само по себе не создаст недостающую страницу.

Комментарии

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

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

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