Раздача статических файлов: root и alias

Отдать файл с диска — самая базовая задача веб-сервера, и Nginx делает это блестяще. Главное — не перепутать root и alias.
«root приклеивает URI к пути целиком, alias — заменяет совпавшую часть. Перепутаешь — получишь 404 на ровном месте.»

Статика — это файлы, которые не меняются от запроса к запросу: HTML, CSS, JS, картинки, шрифты. Nginx читает их прямо с диска и отдаёт максимально быстро. Управляют этим две директивы: root и alias.

Директива root

root задаёт корневой каталог, к которому целиком приклеивается URI запроса:

location /images/ {
    root /var/www/data;
}
# Запрос /images/cat.png  ->  файл /var/www/data/images/cat.png

Обрати внимание: путь /images/ из URI сохраняется и добавляется к root. Итоговый путь = root + URI.

Директива alias

alias работает иначе: он заменяет совпавшую часть location на указанный путь:

location /images/ {
    alias /var/www/data/pics/;
}
# Запрос /images/cat.png  ->  файл /var/www/data/pics/cat.png

Здесь часть /images/ заменяется на /var/www/data/pics/. Итоговый путь = alias + (URI без префикса location).

Индексные файлы

server {
    listen 80;
    server_name example.com;
    root /var/www/html;

    location / {
        index index.html index.htm;
        try_files $uri $uri/ =404;
    }
}

index задаёт файл по умолчанию для каталога (запрос / отдаст /var/www/html/index.html). try_files по очереди пробует варианты: сам файл $uri, затем каталог $uri/, и если ничего нет — отдаёт 404.

Как работает под капотом

Получив запрос, Nginx находит подходящий location, вычисляет путь к файлу (root приклеивает весь URI, alias заменяет префикс), проверяет существование файла и отдаёт его, используя sendfile — копирование из файла прямо в сетевой сокет средствами ядра, без лишнего копирования в память приложения. Это очень дёшево по CPU. Content-Type определяется по расширению через mime.types.

Частые ошибки

  • Использовать root там, где нужен alias. Классика: location /static/ { root /var/www/static/; } приведёт к пути /var/www/static/static/... — двойной static и 404.
  • Забыть слеш в alias. Для location со слешем на конце alias тоже должен оканчиваться слешем.
  • Полагаться на =404 в try_files, забыв саму статику. Проверь права доступа: воркер должен иметь право читать файлы.

Best practices

  • Для подкаталогов с «перепрыгиванием» в другое место используй alias; для прямого совпадения структуры — root.
  • try_files $uri $uri/ =404; — безопасный шаблон: отдай файл, иначе явный 404.
  • Держи статику и аплоады в отдельных каталогах с правильными правами.

SPA, аплоады и защита от обхода путей

На практике раздача статики почти всегда сложнее, чем «отдай файл». Возьмём одностраничное приложение (React, Vue): на сервере лежит один index.html и куча ассетов, но маршрутизация — на клиенте. Если пользователь обновит страницу на /profile/settings, такого файла на диске нет, и обычная отдача вернёт 404. Решение — try_files $uri $uri/ /index.html;: не нашёл файл — отдай index.html, а уж он сам разберётся с маршрутом средствами JavaScript. Это стандартный паттерн для SPA, который стоит знать наизусть.

Второй важный момент — безопасность путей. Когда путь к файлу формируется из URI, возникает риск обхода каталога (path traversal): запрос вида /static/../../etc/passwd теоретически мог бы вылезти за пределы корня. Nginx нормализует путь и не пускает наружу, но коварство alias в том, что при неаккуратной комбинации с регулярными location можно открыть лишнее. Поэтому держи каталог статики изолированным, не клади туда конфиги и ключи, и выдавай воркеру права только на чтение нужных файлов. Аплоады пользователей вообще лучше хранить в отдельном каталоге с запретом на исполнение, чтобы загруженный «аватар» не оказался исполняемым скриптом. Правильная раздача статики — это не только про скорость, но и про то, чтобы случайно не отдать в интернет то, что не предназначено для чужих глаз.

Итоги

Nginx отдаёт статику через root (URI приклеивается к корню целиком) или alias (префикс location заменяется на путь). index задаёт файл по умолчанию, try_files — цепочку проверок. Под капотом работает быстрый sendfile. Дальше — самое важное в маршрутизации: как Nginx выбирает location среди многих.

Проверьте себя
1. В чём разница между root и alias?
AОни идентичны
Broot приклеивает весь URI к корню, alias заменяет совпавший префикс location на указанный путь
Croot для HTTPS, alias для HTTP
Dalias работает только с картинками
2. Что делает `try_files $uri $uri/ =404;`?
AВсегда отдаёт 404
BПробует отдать файл $uri, затем каталог $uri/, и если ничего нет — возвращает 404
CПроксирует на бэкенд
DКеширует ответ